安全 Rust 與不安全 Rust(safe vs unsafe)
你寫的幾乎所有 Rust 都是安全的 Rust:借用檢查器開著、所有權被強制、編譯器保證沒有釋放後使用、沒有懸置參考、沒有資料競爭。但有些事情確實無法在那些規則之內表達——與硬體對話、呼叫 C 函式庫、解參考一個你從作業系統拿到的原始指標、或實作安全程式碼賴以站立的那些最基本構件(像標準的 Vec)。正是為了這些,Rust 給你一個逃生口:unsafe 區塊,寫作 unsafe { ... },在裡頭你為一小組編譯器再也無法替你檢查的操作扛起責任。
理解 unsafe 究竟做了什麼至關重要,因為它的名字言過其實。unsafe 並不會關掉借用檢查器、型別系統、所有權或生命週期——在 unsafe 區塊內,那一切仍完全有效。它做的是解鎖五項原本被禁止的特定額外能力:解參考原始指標(*const T 或 *mut T)、呼叫標記為 unsafe 的函式(包括外部 C 函式)、存取或修改可變的靜態變數、實作 unsafe 特徵、以及存取聯合(union)的欄位。整份清單就這些。在區塊內,你是在向編譯器承諾你已親自檢查過它無法檢查的事——例如,你解參考的那個原始指標真的有效且對齊。若你的承諾錯了,你會得到未定義行為,和 C 裡一模一樣。
為什麼這個設計既誠實又有用:unsafe 不是 Rust 安全性的潰敗,而是 Rust 保持實用的方式。其想法是把無法檢查的部分侷限在小而經審查、清楚標記的區域,再把它們包進一個安全的介面,讓你程式的其餘部分——以及每個呼叫你程式碼的人——重新拿回完整的保證。標準函式庫不斷這麼做:Vec 內部用 unsafe 管理原始記憶體,卻對外暴露一個完全安全的 API。誠實的提醒是它的另一面:unsafe 區塊裡的任何錯誤都可能損壞任何地方的記憶體,而 Rust 的承諾只在你的 unsafe 程式碼信守它那一端的約定時才成立。因此搜尋 unsafe 這個字是審查者做的第一件事——它正好標出了編譯器停止保證、而人必須接手的那些行。
let mut n = 5; let p: *mut i32 = &mut n; // 一個原始指標(安全程式碼中允許) unsafe { *p = 10; // 解參考原始指標「需要」unsafe } println!("{n}"); // 印出 10
unsafe 解鎖原始指標的解參考;它周圍的型別與所有權規則仍然適用。
unsafe 不會停用借用檢查器或型別系統;它只允許五項編譯器無法驗證的額外操作。unsafe 內的錯誤可能在程式任何地方造成 C 式的未定義行為,所以 unsafe 程式碼必須保持精簡並經過審查。