• 为什么说Rust 的设计哲学更符合工程学审美
  • 发布于 2个月前
  • 132 热度
    0 评论
事情源起于我在读龙书时,它讲到了 C 语言 while 语句构建语法树的过程。简化起见,本文我们拿 if 说事儿。

C 语言中,if 是个语句,它的语法定义如下:
if (expr) stmt
书中的脚注讲到:“The right parenthesis serves only to separate the expression from the statement. The left parenthesis actually has no meaning; it is there only to please the eye, since without it, C would allow unbalanced parentheses.” 大概的意思是说:右侧的括号只用于将表达式与语句分隔开。左括号其实没有实际意义,它只是为了让代码看起来更舒服,因为如果没有左括号,C 语言将出现不匹配的括号。

所以在 C 语言中,你可以写出这样的代码:
if (a > b)
    c;
如果 if 后需要执行多条语句,则用 block 即 {} 包围。同类的还有 Java 等等。但是工程实践中你会发现,各语言的风格指南中几乎总是希望你加上 {} ——不论是否为单条语句——以避免不必要的阅读上的麻烦。
if (a > b) {
    c;
}
Rust 的选择要粗暴些,它的 if 条件不使用括号。这一点我们可以从其语法定义中看出:

它没有定义 (、) 这样的 token ,而是在 if 关键字后直接跟表达式(struct 表达式除外),然后跟上 Block 表达式。后面可选的 else 关键字如果紧跟 Block 表达式,则构成了我们常用的 if...else... 结构;如果紧跟 If 表达式则刚好构成 if...else if... ,这个语法递归下去可以形成 if...else if...else if...else... 等。

比较值得注意的是 Rust 选择的 Block 表达式,它的定义如下:

这样一来,{、} 这两个 token 成了必选。也就是说你的代码总是形如:
if xxxx {

}
现在回过头来看为什么 if 后面的条件可以不加小括号?因为其语法定义中没有给 Expression 加括号:)
我们知道 Rust 的表达式本身可以不用括号。但是,表达式也可以用括号啊!是的,你是对的(表达式的语法定义也是这样区分的):
let a = 5;
if (true) {
    println!("{a} > 2");
}
可以编译通过。但编译器会警告我们:
warning: unnecessary parentheses around `if` condition
这一警告不是针对 if 而是表达式本身。形如 let b = (3 + 2); 的代码会得到相同的警告。如果容不下这种写法,可以使用属性 #[deny(unused_parens)] 让编译器强制报错,相应地你也可以更宽容,使用 #[allow(unused_parens)] 。

其实令我好奇的是:Rust 优先把 (true) 当作对表达式 true 加了多余的括号,而不是整体当成一个元组(毕竟元组也是表达式,也符合其语法定义)。倒是有办法让它变成一个元组,而这时也没有了多余的括号的警告:
let a = 5;
if (true,).0 {
    println!("{a} > 2");
}
再来想一个问题,语法定义中 if 后的 Expression 自然也包括 BlockExpression ,那是不是可以:
let a = 5;
if {true} {
    println!("{a} > 2");
}
没错,这样是可以的,只不过是 parentheses 和 braces 的区别:

warning: unnecessary braces around `if` condition


如果你坚持看完这些对你编程能力没有任何提高的废话,你会发现,Rust 的设计哲学更符合工程学审美:它强制你使用 {} 包裹条件分支待执行的代码块,并不推荐你使用 () 包裹条件。但当你无意使用了小括号——毕竟这一习惯被太多的语言保留——它也只是给你一个小小的警告。所以,标题中的问题的答案到底是什么?因为 Rust 已经强制选择了 { ,而这个 token 足以区分 if 的条件和待执行的代码块,Rust 从简的哲学自然会去掉不必要的 ) 。
用户评论