Appearance
19.7 小结
Rust 的 unsafe 关键字是一个难点,也是很多初学者困惑的地方。很多人有这样的疑惑:既然 Rust 允许使用 unsafe 来完成许多危险的操作,那么 Rust 的安全性保证是不是就没什么意义了?
这件事情不能这么理解。unsafe的存在不是来故意破坏安全性的,它只是一种面向更底层操作的接口。不同的高级语言对于什么是底层的定义是不同的,但是所有的高级语言,只要不断往底层探究,总会碰到safe与unsafe之间的分界线。比如,Java有自己的JNI机制,C#也有unsafe关键字,Python也可以调用C模块,甚至C/C++语言都可以内嵌汇编。当你在高级语言中与底层操作交互的时候,必须确保高级语言中的一些规则和约定。Java、C#这类语言,利用GC实现了内存安全,但是用户同样可以使用JNI/unsafe实现不安全的操作,但这件事情并不意味着Java、C#语言本身有安全性缺陷。同理,在C语言里面用内嵌汇编搞乱了堆栈,也不能说是C语言的设计缺陷。只不过是用户使用这些机制的时候,没有一个自动检查工具来保证安全性,而是必须由自己来保证上层代码和下层代码之间交互的正确性。
Rust 的 unsafe 最大的问题在于,到目前为止,依然没有一份官方文档来明确哪些东西是用户可以依赖的、哪些是编译器实现相关的、哪些是以后永远不变的、哪些是将来可能会有变化的。所以,哪怕用户能确保自己写出来的 unsafe 代码在目前版本上是完全正确的,也没办法确保不会在以后的版本中出问题。如果以后编译器的实现发生了变化,导致了 unsafe 代码无法正常工作,究竟算是编译器的 bug 还是用户错误地依赖了某些特性,还说不清楚。正式的 unsafe guideline 还在继续编写过程中。(当然这种错误情况几率是很低的,绝大多数用户使用 unsafe 的时候都是在 FFI 场景下,不会涉及那些精微细密的语义规则。)
我们既不能过于滥用 unsafe,也不该对它心怀恐惧。它只是表明,某些代码的安全性依赖于某些条件,而我们无法清晰地在代码中表达这些约束条件,因此无法由编译器帮我们自动检查。
unsafe 是 Rust 的一块重要拼图,充分理解 unsafe 的意义和作用,才能让我们更好地理解 safe 的来源和可贵。
不尽知用兵之害者,则不能尽知用兵之利也。—— 孙子兵法