通常,在if
表达式中的每个分支都应具有相同的类型。如果某个分支的类型未指定,则编译器会尝试找到单一的通用类型:
fn print_number(x: int, y: int) {
let v = if x + y > 20 {
3
} else {
x + y
};
println!("{}", v);
}
在这段代码中,
3
的类型未指定,但
else
分支将它强制转换为
int
类型。
听起来很简单:有一个函数可以将两个或多个类型统一成一个公共类型,如果不可能进行统一,则会报错。但是,如果分支中存在
fail!
会怎样呢?
fn print_number(x: int, y: int) {
let v = if x + y > 20 {
fail!("x + y too large")
} else {
x + y
};
println!("{}", v);
}
我希望
fail!
不会影响其他分支,毕竟这是一个特殊情况。由于这种模式在Rust中很常见,因此引入了
发散类型的概念。没有值的类型是发散的。(根据上下文,它也被称为“未被占用的类型”或“空类型”。不要与“单元类型”混淆,后者只有一个值
()
。)由于发散类型自然是任何其他类型的子集,编译器得出
v
的类型就是
else
分支的类型
int
。
return
表达式在类型检查方面与
fail!
没有区别。它像
fail!
一样突然跳出当前执行流(但不会终止任务,谢天谢地)。但是,发散类型不会传播到下一条语句:
fn print_number(x: int, y: int) {
let v = if x + y > 20 {
return;
()
} else {
x + y
};
println!("{}", v);
}
请注意,唯一带分号的语句
x;
等同于表达式
x; ()
。通常
a; b
与
b
具有相同的类型,因此当
x
不发散时,
x; ()
具有
()
类型是非常奇怪的,而当
x
发散时,它会发散。这就是为什么您的原始代码无法工作的原因。
添加特殊情况似乎很诱人:
- 为什么不在
x
发散时使x; ()
发散?
- 当无法推断出整数文字的类型时,为什么不假定每个未指定的整数文字都是
uint
?(注:过去是这种情况。)
- 在统一多个trait对象时,为什么不自动查找公共超级trait?
事实上,设计类型系统并不是很难,但验证它要困难得多,我们希望确保Rust的类型系统是面向未来的和长期存在的。如果某些内容确实有用并且被证明对我们的目的是“正确”的,则可能会发生其中一些,但不会立即发生。
return
、fail!
和发散函数的工作原理了。我的代码示例在return x
处是否有分号都可以编译通过。所以我猜结论是:加上分号也不会有什么区别? - sargasreturn x
还是return x;
在最后似乎都做了同样的事情,我不知道在这种情况下使用分号有什么区别。我的编辑是否改变了您的问题背景?(仍在努力理解Rust的低级概念)。 - sargasif
表达式;现在,函数的尾部是x + y
表达式。在两种情况下,return
表达式都可能导致早期退出。然而,在你编辑的示例中,if
表达式的类型变得无关紧要(我们知道它是()
,因为它没有else
子句,只有当if
子句评估为()
时才允许)。话虽如此,就个人而言,如果我用return
结束一个if
,我不会写一个else
,而是把那段代码放在if
后面,就像你在你的编辑中所做的那样。 - Francis Gagné