Rust 有一个叫做 ! 的特殊类型。在类型理论术语中,它被称为 empty type,因为它没有值。我们更倾向
于称之为 never type。这个名字描述了它的作用:在函数从不返回的时候充当返回值。例如:
fn bar() -> ! {
// --snip–
panic!();
}
这读 ” 函数 bar 从不返回”,而从不返回的函数被称为 发散函数(diverging functions)。不能创建 ! 类型
的值,所以 bar 也不可能返回值。
不过一个不能创建值的类型有什么用呢?如果你回想一下示例 2-5 中的代码,曾经有一些看起来像这样
的代码,如示例 19-26 所重现的:
use rand::Rng;
use std::cmp::Ordering;
use std::io;
19.3. 高级类型 507
fn main() {
println!(“Guess the number!”);
let secret_number = rand::thread_rng().gen_range(1…101);
println!(“The secret number is: {}”, secret_number);
loop {
println!(“Please input your guess.”);
let mut guess = String::new();
// --snip–
io::stdin()
.read_line(&mut guess)
.expect(“Failed to read line”);
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!(“You guessed: {}”, guess);
// --snip–
match guess.cmp(&secret_number) {
Ordering::Less => println!(“Too small!”),
Ordering::Greater => println!(“Too big!”),
Ordering::Equal => {
println!(“You win!”);
break;
}
}
}
}
示例 19-26: match 语句和一个以 continue 结束的分支
当时我们忽略了代码中的一些细节。在第六章 ”match 控制流运算符” 部分,我们学习了 match 的分支
必须返回相同的类型。如下代码不能工作:
fn main() {
let guess = “3”;
let guess = match guess.trim().parse() {
Ok() => 5,
Err() => “hello”,
};
}
这里的 guess 必须既是整型 也是字符串,而 Rust 要求 guess 只能是一个类型。那么 continue 返回了
什么呢?为什么示例 19-26 中会允许一个分支返回 u32 而另一个分支却以 continue 结束呢?
正如你可能猜到的,continue 的值是 !。也就是说,当 Rust 要计算 guess 的类型时,它查看这两个分
支。前者是 u32 值,而后者是 ! 值。因为 ! 并没有一个值,Rust 决定 guess 的类型是 u32。
描述 ! 的行为的正式方式是 never type 可以强转为任何其他类型。允许 match 的分支以 continue 结束
508 CHAPTER 19. 高级特征
是因为 continue 并不真正返回一个值;相反它把控制权交回上层循环,所以在 Err 的情况,事实上并未
对 guess 赋值。
never type 的另一个用途是 panic!。还记得 Option 上的 unwrap 函数吗?它产生一个值或 panic。
这里是它的定义:
enum Option {
Some(T),
None,
}
use crate::Option:😗;
impl Option {
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!(“called Option::unwrap()
on a None
value”),
}
}
}
这里与示例 19-34 中的 match 发生了相同的情况:Rust 知道 val 是 T 类型,panic! 是 ! 类型,所以整
个 match 表达式的结果是 T 类型。这能工作是因为 panic! 并不产生一个值;它会终止程序。对于 None
的情况,unwrap 并不返回一个值,所以这些代码是有效的。
最后一个有着 ! 类型的表达式是 loop:
fn main() {
print!("forever ");
loop {
print!("and ever ");
}
}
这里,循环永远也不结束,所以此表达式的值是 !。但是如果引入 break 这就不为真了,因为循环在执
行到 break 后就会终止。