Skip to content

13.5 小结

Rust 在内存安全方面的设计方案的核心思想是“共享不可变,可变不共享”。

在可变性控制方面,如果说,C 语言和函数式编程语言分属一个天平的两端,那么 Rust 就处于这个天平的中央。 C 语言的思想是:尽量不对程序员做限制,尽量接近机器底层,类型安全、可变性、共享性都由程序员自由掌控,语言本身不提供太多的限制和规定。安全与否,也完全取决于程序员。 而函数式编程的思想是:尽量使用不可变绑定,在可变性上有严格限制,在共享性方面没有限制。函数式编程特别强调无副作用的函数以及不可变类型,以此来达到提高安全性的目的。

而 Rust 则是选择了折中的方案,既允许可变性,也允许共享性,只要这两者不是同时出现即可。“共享不可变,可变不共享”,是 Rust 保证内存安全和线程安全的“法宝”。 而我们可以看到,Rust 的这个设计并不是首鼠两端、和稀泥式的中庸之道,而是经过了仔细的观察总结、严谨的设计之后的产物。

其一,相比函数式设计方式,Rust 并没有本质上牺牲安全性。函数式编程强调的“不可变”特性,极大地提升了安全性的同时,也极大地提高了学习门槛。 而 Rust 在“不可变”要求上的理性妥协,实现了在不损失安全性的同时,一定程度上也降低了学习成本。从 C/C++背景转为使用 Rust 无需做太大的思维转变。 相比函数式的设计方式,Rust 的入门门槛更低。虽然对于习惯了无拘无束自由挥洒的 C/C++编程语言的朋友来说,还是有诸多不习惯,但毕竟比 Haskell 要容易得多。

其二,Rust 针对传统 C/C++做了大幅改进,设计了一系列静态检查规则,来防止一些潜在的 bug。“共享不可变,可变不共享”就是其中一项重要的规则。 在传统的 C/C++中,所有的指针都是同一个类型。从功能性来说,这样设计是非常强大的,但它缺少的恰恰是一定程度的取舍,以提高安全性。 相对来说,Rust 对程序员的限制更多,有所为、有所不为。鼓励用户使用的功能应当越容易越优雅越好;避免用户滥用的功能应当越困难越复杂越好。二者不可偏废。

其三,Rust 的这套内存安全体系,不需要依赖 GC。虽然现在 GC 的性能越来越好,但是没有 GC 在某些场景下依然是很重要的。 没有 GC、编译型语言的特点,是 Rust 执行性能的潜力保证。这就是为什么 Rust 设计组有底气说 Rust 的运行性能与 C 语言处于同一个档次的原因。 当然,目前的 Rust 还很年轻,许多优化还没有实现,但这不要紧,单从技术层面上看,还有许多优化在可行性上是没问题的,唯一需要的是时间和工作量。 另外,没有 GC 就可以使得它只依赖一个非常轻量级的 runtime。 理论上来说,它可以用于许多嵌入式平台,甚至可以在无操作系统的裸机上执行,使用 Rust 编写操作系统也是完全可行的。 这就使得 Rust 拥有与 C/C++相似的系统级编程特性,大幅扩展了 Rust 的应用场景。

其四,Rust 的核心思想“共享不可变,可变不共享”,具有极好的一致性和扩展性。它不仅可以解决内存安全的问题,还是解决线程安全的基础。 在后文中我们会看到,所谓的线程安全,实质上就是内存安全在多线程情况下的自然延伸。 反过来,我们也可以把 Rust 的内存安全解决方案视为传统的线程安全机制 Read Write Locker 的编译阶段执行的版本。 大家应该都能联想到,在多线程环境下,数据竞争问题是怎么出现的。如果多个线程对同一个共享变量都是只读的,它是安全的; 如果有一个线程对共享变量写操作,那它就必须是独占的,不可有其他线程继续读写,否则就会出现数据竞争。 在第四部分中我们还会发现,Rust 里面的许多线程安全的类型,与一些非线程安全的类型,具有非常有趣的对称性。

由此我们可以看出,Rust 的这套设计方案的确是有创新性的。它走出了一条前无古人的道路。Rust 在其他方面的功能,都不能被称作原创设计,都是从其他编程语言中学过来的。 唯独安全性方面的设计是独一无二的。只要我们保证了“共享不可变,可变不共享”,我们就可以保证内存安全。那么它这套设计方案,究竟能不能被大众所接受呢?我们拭目以待。

另外,这个规定是否是过于严苛了呢?会不会大幅削弱代码的表达能力?后面我们还需要进一步分析。

Released under the MIT License