理清头脑混沌,觉醒心智天地
我不太清楚 「Rust 往事」会不会是一个系列,先建个专栏再说。 本来想把 「往事」限定于 1.0 版本之前,但又觉得太狭隘了。Rust 是一门不断向前发展的语言,所谓往事,我觉得应该是 Rust 在发展过程中,经历过的所有决策和讨论。 一切过往,皆为序章。
引子
我曾经一直想不明白一个问题:为什么下面代码中 while true 无法通过编译 ?
fn main() {
let mut a;
while true {
a = 1;
break ;
}
println!("{}", a); // error[E0381]: borrow of possibly-uninitialized variable: `a`
}
而将 while true 换为 loop 则可以通过编译:
fn main() {
let mut a;
loop {
a = 1;
break ;
}
println!("{}", a);
}
我清楚它们之间的区别, while true 在编译期静态分析的时候和 loop 不太一样。
前者因为是 while 条件,所以编译器无法在编译期确定它的值到底是不是 true,因为 while 表达式的条件位置并非一个 常量上下文,所以无法在编译期求值,而只能检查类型。
后者则因为是永久循环,必然会执行一次,何况这里又加了 break,所以编译器会在编译期将 a 直接初始化为常量 1。即便这里不加 break,编译器也会进行初始化,大不了加个 goto 指令一直初始化。
之前 const fn 中无法使用 while 也就罢了,现在 1.46 版本中 const fn 增加了对 if/ while 的支持,可为什么还不愿意(并非识别不了)识别 while true 呢?
loop 和 while true 之争
于是,我翻了一下 Rust 语言源码仓库里的issues,找到一条有意思的 issues #12975:
「Remove `loop` keyword from the language」 ,地址:https://github.com/rust-lang/rust/issues/12975。
什么情况?从 Rust 中移除 loop 关键字 ?仔细看了一下,才发现是 2014 年的。该 Issues 建议:
这个建议看上去,好像是挺有道理。于是,另外一个人就马上响应,写了一份 RFC:「Propose replacing the `loop` keyword with `while true`」,地址:https://github.com/rust-lang/rfcs/pull/429。
这个 RFC 当然没有被通过审核,否则现在就看不到 loop 关键字了,官方团队以「没有兴趣删除 loop 为由,关闭了此 RFC」。看当时 RFC PR 下面的讨论,nrc 其实说了具体的理由:「这种更改,其实是在区别对待 while <condition == true > 和 while true」,这种设计比较粗鲁。假如 while 后面加了一个被识别为 true 的常量表达式,用不用通过编译?
Rust 语言设计的原则之一,就是高度一致性。这样的设计就搞的很迷。
从现在这个时间节点上回头看,Rust 官方没有接受这个 RFC 是对的。
至此,我的问题也迎刃而解:
虽然在编译期识别 true 字面量易如反掌,但整体来看,while true 其实属于一种特殊的情况,更为普遍的是 while (constexpr == true) 的情况,如果后者的条件表达式越复杂越难判断到底是不是 true。
为了保持语言的一致性,就不能给 while true 开小灶。语言缺乏一致性,对于开发者来说,其实是一种灾难。
妥协
即便如此,while true 依然是个问题。
于是 Rust 团队增加了一个 lint : #[warn(while_true)] ,默认情况下是 warn,但也可以使用,#[deny(while_true)] 和 #[allow(while_true)] 。
warning: denote infinite loops with `loop { ... }`
--> src/lib.rs:6:5
|
6 | while true {
| ^^^^^^^^^^ help: use `loop`
|
= note: `#[warn(while_true)]` on by default
当你习惯性使用 while true 的时候,Rust 会以警告的方式提示你:无限循环请使用 loop。但是你执意要使用 while true 的话,就最好加上 `#[allow(while_true)]` 。然而,我其实更喜欢用 loop,简单明了。
这其实也算是 Rust 语言设计上的一种妥协吧。原因有二:
像这样通过 lint 的方式来提示开发者也是一种很好的方式,美中不足的是,这个 warning 还缺乏一个解释,解释为什么 while true 和 loop 的这个区别。这也是为什么有这篇文章的原因。
小结
从这件小故事中看得出来,Rust 语言团队坚守语言设计原则,才能有现在的 Rust。这样的争论,其实在 Rust 社区发生过不少次,比如稳定异步特性的过程中,Pin的引入,以及 async/await 语法的设计等等。
希望后面有时间可以继续挖掘这些过往的故事。