最近学习Rust,在看完了官网上的Rust Book之后,我买了一本张汉东老师著的《Rust编程之道》,来两本书对照着再看一遍。

今天要说的,就是发生在阅读《Rust编程之道》时发生的一个我认为很有意思的小事。

本文标题中提到“抬杠”,实际上我并不是就书的某些内容跟作者或其他读者抬杠,而是与自己抬杠。

起因是我读到的该书中的一段话,原文如下:

条件表达式一定会有值,所以if表达式的分支必须返回同一个类型的值才可以。这也是Rust没有三元操作符?:的原因。

由于我之前已经完整的读过了一本Rust教材,而且也有其他语言的编程基础,所以这句话里传达的知识点本身我都已经是明白的。但读完后第一感觉就是不顺。不知道其他人看到这一段话第一反应会怎么理解,我的心理过程是这样的:

第一句,“条件表达式一定会有值,所以if表达式的分支必须返回同一个类型的值才可以”,没问题,这一句很简单,跳过。

第二句,“这也是Rust没有三元操作符?:的原因”,看上去有点意思,关于来龙去脉的东西要好好看一下,回顾一下上一句,上一句是”所以if表达式的分支必须返回同一个类型的值才可以”。有一个必须哦,所以说Rust没有三元操作符是因为三元操作符不满足这个必须的要求?也就说,因为if表达式必须返回同一个类型的值,但三元操作符不满足这个要求,所以Rust中没有三元操作符?

我心里马上咯噔一下,三元操作符还有这毛病?印象中不是这样啊。于是随手写了几行c代码用gcc验证了一下,更坚定我的信心了,至少在c里面,三元操作符是没有这个问题的。于是我在微信里向张汉东老师本人求证:

汉东老师,在2.5.1条件表达式这一节,书里说 “表达式一定会有值,所以if表达式的分支必须返回同一个类型的值才可以。这也是Rust没有三元操作符?:的原因。”

读起来感觉这一句话有点问题,感觉您想表达因为三元操作符可能会返回不同类型的值,所以才没有支持三元操作符。

一方面,在c里面,虽然int a = 1 == 2 ? 2 : “aa”;这样的语句编译器不会报错,但是会报类型不匹配和一个从指针到整数的类型转换的警告,而且这个其实跟三目运算本身没关系,直接用int a = “aa”,也是会涉及到相同的警告,即类型不匹配和一个从指针到整数的类型转换。

另一方面,我查了一下rust以前的issues,比如 https://github.com/rust-lang/rfcs/issues/1362 (不止这一个),里面的讨论也大部分都是基于有没有必要在if else满足条件的情况下引入一个三目操作,而且rust之前的版本里本来是有三目,后来有觉得没必要所以去掉了。

总之我想表达的是可能rust没有三元操作符不是因为这个原因,不知道我理解有没有问题。谢谢:)

张汉东老师很快就回复我了:

你想太多了。我想表达的就是两点:

  1. if是个表达式。表达式自然要返回同一个类型的值。
  2. 有了这个表达式就没必要引入三元操作符了(所以,没有三元操作符),你看看Rust的语法,基本上是避免同一件事引入多种方法。再加上三元操作符中的两个符号「:」和「?」,在Rust里会破坏一致性。

这个回复非常清楚,没有任何问题和歧义。本身这几个知识点我是理解的,所以我非常好奇,是什么原因导致我把他的原句理解成了我上面所说的样子,这个原因必须找出来。

再次回顾一下原文:

条件表达式一定会有值,所以if表达式的分支必须返回同一个类型的值才可以。这也是Rust没有三元操作符?:的原因。

顿时恍然大悟。

这两句话的最重要的地方是第一句话的前半部分,即“条件表达式一定会有值”,基于此,会产生两个结果,其一是if表达式的分支必须返回同一个类型的值,其二是Rust中不必有三元操作符。这两个结果的关系是并列的。

而我之前对这段话的理解错误,根源在于把第二句话中提到的原因归结到了第一句话的后半句,实际应该是前半句。而为什么我会第一反应做出如此的理解呢?原因在于第一句后半句中的那个必须一词,它的语气很强,把我的注意力吸引到它上面,而失去了对这一段话整体结构的把握。

上面就是我跟自己”抬杠“的完整过程。这个过程不由让我想到《如何阅读一本书》中的一个比喻,在一场阅读的过程中,作者与读者就像是棒球赛场中的一对投手和捕手。写作与阅读的东西就像那只球一样,是被主动、有活力的双方所共有的,由一方开始,另一方终结。

读一本书,能完全读懂作者在写作时的心理状态和想表达的内容,真不是一件容易的事情。逻辑性很强的一本编程书都是如此,其他领域,尤其文学作品中就更难了。我想也正因为如此,才导致了一千个读者就有一千个哈姆雷特吧。