安全(Safety)
本章不讲解任何语言知识点,而是对 Rust 安全理念的一些总结性说明。
安全,本身是一个相当大的话题。安全性,本身也需要一个局部性的定义。
Rust 的定义中,凡是 可能 会导致程序内存使用出错的特性,都被认为是 不安全的(unsafe)。反之,则是 安全的(safe)。
基于这种定义,C 语言,基本是不安全的语言(它是众多不安全特性的集合。特别是指针相关特性,多线程相关特性)。
Rust 的这个定义,隐含了一个先决假设:人之初,性本恶。人是不可靠的,人是会犯错误的,即 Rust 不相信人的实施过程。在这一点上,C 语言的理念与之完全相反:C 语言完全相信人,人之初,性本善,由人进行完全地控制。
根据 Rust 的定义,C 语言几乎是不安全的代名字。但是,从本质上来说,一段程序是否安全,并不由开发它的语言决定。用 C 语言开发出的程序,不一定就是不安全的代码,只不过相对来说,需要花更多的精力进行良好的设计和长期的实际运行验证。Rust 使开发出安全可靠的代码相对容易了。
世界本身是肮脏的。正如,纯函数式语言中还必须有用于处理副作用的 Monad
存在一样,Rust 仅凭安全的特性集合,也是无法处理世界的所有结构和问题的。所以,Rust 中,还有 unsafe
部分的存在。实际上,Rust 的 std 本身也是建立在大量 unsafe
代码的基础之上的。所以,世界就是纯粹建立在不纯粹之上,“安全”建立在“不安全”之上。
因此,Rust 本身可以被认为是两种编程语言的混合:Safe Rust
和 Unsafe Rust
。
只使用 Safe Rust
的情况下,你不需要担心任何类型安全性和内存安全性的问题。你永远不用忍受空指针,悬挂指针或其它可能的未定义行为的干扰。
Unsafe Rust
在 Safe Rust
的所有特性上,只给程序员开放了以下四种能力:
- 对原始指针进行解引(Dereference raw pointers);
- 调用
unsafe
函数(包括 C 函数,内部函数,和原始分配器); - 实现
unsafe
traits; - 修改(全局)静态变量。
上述这四种能力,如果误用的话,会导致一些未定义行为,具有不确定后果,很容易引起程序崩溃。
Rust 中定义的不确定性行为有如下一些:
- 对空指针或悬挂指针进行解引用;
- 读取未初始化的内存;
- 破坏指针重命名规则(比如同一资源的
&mut
引用不能出现多次,&mut
与&
不能同时出现); - 产生无效的原生值:
- 空指针,悬挂指针;
- bool 值不是 0 或 1;
- 未定义的枚举取值;
- char 值超出取值范围 [0x0, 0xD7FF] 和 [0xE000, 0x10FFFF];
- 非 utf-8 字符串;
- Unwinding 到其它语言中;
- 产生一个数据竞争。
以下一些情况,Rust 认为不属于安全性的处理范畴,即认为它们是“安全”的:
- 死锁;
- 存在竞争条件;
- 内存泄漏;
- 调用析构函数失败;
- 整数溢出;
- 程序被中断;
- 删除产品数据库(:D);
参考
下面一些链接,给出了安全性更详细的讲解(部分未来会有对应的中文翻译)。