我已经阅读了几个关于 SO 的答案,并收集了这些用例:
- 当函数
panic!
s - 当函数中有无限循环时
但我仍然不清楚为什么我们需要这样定义函数:
fn func() -> ! {
panic!("Error!");
}
如果它的工作方式与此相同(没有感叹号):
fn func() {
panic!("Error!");
}
同时,为什么我们需要在无限循环的函数中使用 !
?看起来这个签名并没有带来任何真实的使用信息。
我已经阅读了几个关于 SO 的答案,并收集了这些用例:
panic!
s但我仍然不清楚为什么我们需要这样定义函数:
fn func() -> ! {
panic!("Error!");
}
如果它的工作方式与此相同(没有感叹号):
fn func() {
panic!("Error!");
}
同时,为什么我们需要在无限循环的函数中使用 !
?看起来这个签名并没有带来任何真实的使用信息。
这些签名之间的主要区别归结为 !
可以 coerce
转换为任何其他类型,因此 compatible
可以转换为任何其他类型(由于从未采用此代码路径,我们可以假设它是我们需要的任何类型)。当我们有多个可能的代码路径时,这一点很重要,例如 if-else
或 match
。
例如,考虑以下(可能是人为的,但希望足够清晰)代码:
fn assert_positive(v: i32) -> u32 {
match v.try_into() {
Ok(v) => v,
Err(_) => func(),
}
}
当 func
声明返回 !
时,此函数 compiles 成功。如果我们删除返回类型, func
将被声明为返回 ()
,并且编译 breaks :
error[E0308]: `match` arms have incompatible types
--> src/main.rs:8:19
|
6 | / match v.try_into() {
7 | | Ok(v) => v,
| | - this is found to be of type `u32`
8 | | Err(_) => func(),
| | ^^^^^^ expected `u32`, found `()`
9 | | }
| |_____- `match` arms have incompatible types
您还可以将其与 definition for Result::unwrap
进行比较:
pub fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
}
}
这里是 unwrap_failed
is returning !
,因此它与 Ok
情况下返回的任何类型相统一。
编译器知道,遵循不同表达式(W.R.T.评估顺序)的任何内容都是无法实现的。在确定局部变量是否初始化时,它可以使用此信息来避免虚假负面。
考虑以下示例:
我们声明一个变量
foo
,但我们仅在if
表达式的一个分支中初始化它。这未能与以下错误进行编译:但是,如果我们更改
diverge
函数的定义:然后代码成功编译。编译器知道,如果
else
分支被取下,它将永远不会到达println!
,因为diverge()
偏离。因此,else
分支不初始化foo
不是错误。