Published on

🦀 写 Rust 一年有余,浅谈对它的感受

Authors
  • avatar
    Name
    阿森 Hansen
    Twitter

初步感受

从第一次接触 Rust 到现在,已经快两年了。总体来讲,这是一个严谨,值得反复玩味的语言。越写越觉得有意思。 ok 用编程语言解决问题本身就是挺有挑战的工作,而 Rust 有自己的一套严谨的规则,所以写代码的时候,除了要保证能正常运行,还必须遵守 Rust 的独特规则。

这就让写代码这件事情做起来像是在玩游戏。Rust 编译器给你出题,而你必须解题。

我是通过《Rust 圣经》这本开源书学习 Rust 的,它的每一章节结束后都会有练习题,而这些练习题往往让我玩得津津有味。这本书十分有意思,建议你也看看。

链接放在这里:关于本书 - Rust语言圣经(Rust Course)

Rust 和其他语言的不同

我是一名编程语言爱好者,同时也是全栈工程师。我喜欢关于计算机的一切,从算法,数学,架构到产品。基本主流的编程语言我都用过 C,Java,Python,JS,Go,然后是这个 Rust。(导致了我电脑的硬盘空间常常不够用……)

不得不说,Rust 是一个非常有意思的语言,它的设计理念和其他所有的语言都不同。例如,C 和汇编语言最像,也最接近计算机硬件,Java 是一个超级面向对象语言,啥都是类都是对象,Python 则是一个用户友好语言(但太过于用户友好以至于对计算机没那么友好……)。

而 Rust,是一个“理性”的语言,它的设计就像是一本亚里士多德的逻辑学著作,思路清晰,结构严谨。它将编程开发的“最佳实践”强制固定下来,用这种方式保证了程序执行的安全和效率。

然而,Rust 的缺点也很明显,因为“最佳实践”被强制要求了,所以 Rust 对于程序员的要求是很高的。不仅要求学习很多复杂的知识点,还要深刻理解计算机底层的工作原理。

不过,作为编程语言爱好者,这也很有意思不是吗?😄

这是我的 Rust 笔记,比起 Python,Java 和 Go,它的笔记量大多了

写 Rust 要注意的问题

Rust 的众多独特概念既复杂又难懂,所以我不会在这篇文章里说太多。我只说说我在写 Rust 一年后的三点经验。

第一,定义变量之前,要想清楚这个变量的有效范围。

Rust 中,所有的变量对应的内存空间是有所有权的,读和写都被严格控制。不像 Python Java 这些有垃圾回收机制的语言,一个地方定义的变量可以到处传递,其他的交给垃圾回收来管理。

Rust 没有垃圾回收,所以它必须知道什么时候应该分配内存,什么时候应该回收。不同于 Python 和 Java,Rust 用了一种更“傻”的方式,就是保证一个变量的所有权仅在一个地方可用,当程序执行从这个地方离开时,其内部的所有变量都会被回收。

所以在 Rust 中 let 一个新的变量前,你应该很清楚它的作用范围,以及它在什么时候会被回收。如果需要延伸它的作用范围,一般来说你需要 Clone 这个变量。

如果在写代码之前没想清楚如何设计变量,你就需要花大量的时间重构代码,来解决编译器报错。(没错,我就在这上面踩了很多坑)

第二,使用引用(指针)之前,考虑读写的先后顺序,避免冲突

用一句话概括 Rust 中对引用的限制:读引用(不可变引用)和写引用(可变引用 mut)不可共存,读引用可以有多个,写引用必须只有一个

这样一来,引用的先后顺序就很重要了。如果你要用引用读一个变量,那么得确保写的引用已经释放了。相反也一样,如果你要写一个变量,那么得确保读的引用已经被释放。这和其他语言完全不同,例如 Python 就完全不用担心读写之间会产生冲突的问题。 Rust 是我使用的编程语言中,遇到的唯一一个对顺序要求如此高的语言。

这个有点像在并发编程中用锁来解决竞争条件,如果有高并发编程经验的同学可能更好理解一些。

需要进一步了解,可看:🦀 简单讲讲 Rust 多线程中的引用安全 | 阿森的知识图谱

第三,使用多线程之前,考虑线程间的数据使用关系。

这点非常关键,也是我第一次在写 Rust 时踩过最大的坑。因为问题编译器不太能够给你很好的建议,只能告诉你哪里出现了变量逃逸,但是不会给出明确的解决方法。

怎么办呢?只能回到起点,画画图,确认哪些线程要用哪些数据?哪些数据可以共享而哪些不能?线程什么时候启动什么时候结束?

对于需要共享的数据,一般要用 Arc 和 Mutex 组合做带引用计数的互斥锁。对于不能共享的数据,则要在线程启动之前拷贝,并传递到线程内。

之前有篇文章就写了关于此的内容:🦀 Rust 多线程报错:error[E0521] borrowed data escapes outside of method | 阿森的知识图谱

总结:这一切值得吗

当然我踩的坑很多,以上三点只是我觉得最明显的。

Rust 不仅仅要花大量时间学习,为了通过编译,还要花大量时间重构,还有很多坑。一个简单的功能,换用其他语言,半小时就能写好的,Rust 可能要花半天。

所以,这样做值得吗?

我认为是值得的。我们虽然在前期花了大量工作,然而 Rust 一旦通过编译,就能保证代码质量,避免内存泄露、悬空指针等问题。而这些问题,若在 C 中出现,则要花费大量时间调试。

我们只是把同样的问题放在前面解决了。

这是一个不错的生活哲学,因为“机会总青睐有准备的人”。把事情做在前面,也是很好的工作态度。