安全(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);
參考
下面一些鏈接,給出了安全性更詳細的講解(部分未來會有對應的中文翻譯)。