unsafe

Rust的內存安全依賴於強大的類型系統和編譯時檢測,不過它並不能適應所有的場景。 首先,所有的編程語言都需要跟外部的“不安全”接口打交道,調用外部庫等,在“安全”的Rust下是無法實現的; 其次,“安全”的Rust無法高效表示複雜的數據結構,特別是數據結構內部有各種指針互相引用的時候;再次, 事實上還存在著一些操作,這些操作是安全的,但不能通過編譯器的驗證。

因此在安全的Rust背後,還需要unsafe的支持。

unsafe塊能允許程序員做的額外事情有:

  • 解引用一個裸指針*const T*mut T
let x = 5;
let raw = &x as *const i32;
let points_at = unsafe { *raw };
println!("raw points at {}", points_at);
  • 讀寫一個可變的靜態變量static mut
static mut N: i32 = 5;
unsafe {
    N += 1;
    println!("N: {}", N);
}
  • 調用一個不安全函數
unsafe fn foo() {
    //實現
}
fn main() {
    unsafe {
        foo();
    }
}

使用unsafe

unsafe fn不安全函數標示如果調用它可能會違反Rust的內存安全語意:

unsafe fn danger_will_robinson() {
    // 實現
}

unsafe block不安全塊可以在其中調用不安全的代碼:

unsafe {
    // 實現
}

unsafe trait不安全trait及它們的實現,所有實現它們的具體類型有可能是不安全的:

unsafe trait Scary { }
unsafe impl Scary for i32 {}

safe != no bug

對於Rust來說禁止你做任何不安全的事是它的本職,不過有些是編寫代碼時的bug,它們並不屬於“內存安全”的範疇:

  • 死鎖
  • 內存或其他資源溢出
  • 退出未調用析構函數
  • 整型溢出

使用unsafe時需要注意一些特殊情形:

  • 數據競爭
  • 解引用空裸指針和懸垂裸指針
  • 讀取未初始化的內存
  • 使用裸指針打破指針重疊規則
  • &mut T&T遵循LLVM範圍的noalias模型,除了如果&T包含一個UnsafeCell<U>的話。不安全代碼必須不能違反這些重疊(aliasing)保證
  • 不使用UnsafeCell<U>改變一個不可變值/引用
  • 通過編譯器固有功能調用未定義行為:
    • 使用std::ptr::offset(offset功能)來索引超過對象邊界的值,除了允許的末位超出一個字節
    • 在重疊(overlapping)緩衝區上使用std::ptr::copy_nonoverlapping_memory(memcpy32/memcpy64功能)
  • 原生類型的無效值,即使是在私有字段/本地變量中:
    • 空/懸垂引用或裝箱
    • bool中一個不是false(0)或true(1)的值
    • enum中一個並不包含在類型定義中判別式
    • char中一個代理字(surrogate)或超過char::MAX的值
    • str中非UTF-8字節序列
  • 在外部代碼中使用Rust或在Rust中使用外部語言

results matching ""

    No results matching ""