018 Rust死靈書之非安全方式初始化記憶體

linghuyichong發表於2021-03-07

本系列錄製的影片主要放在B站上Rust死靈書學習影片

Rust 死靈書相關的原始碼資料在github.com/anonymousGiga/Rustonomi...

安全 Rust 不允許部分地初始化陣列,初始化一個陣列時,透過let x = [val; N] 為每一個位置賦予相同的值,或者是單獨指定每一個成員的值 let x = [val1, val2, val3]。但是有時我們需要用增量或者動態的方式初始化陣列。

非安全 Rust 提供了mem::uninitialized處理這一問題。這個函式假裝返回一個值,可以用它來欺騙 Rust 我們已經初始化了一個變數了。

特別注意

此方式會帶來安全隱患。因為在Rust中,未被初始化的記憶體,賦值時是將位元組複製到對應的記憶體;而對於被初始化過的記憶體,賦值時除了複製外還需要銷燬原來的值。

解決方式,透過如下三個函式:

1、ptr::write(ptr, val) 函式接受 val 然後將它的值移入 ptr 指向的地址
2、ptr::copy(src, dest, count) 函式從 src 處將 count 個 T 佔用的位元組複製到 dest。(這個函式和 memmove 相同,不過要注意引數順序是反的!)
3、ptr::copy_nonoverlapping(src, dest, count) 和 copy 的功能是一樣的,不過它假設兩段記憶體不會有重合部分,因此速度會略快一點。(這個函式和 memcpy 相同,不過要注意引數順序是反的!)
//例子
use std::mem;
use std::ptr;

fn main() {
    // 陣列的大小是硬編碼的但是可以很方便地修改
    // 不過這表示我們不能用[a, b, c]這種方式初始化陣列
    const SIZE: usize = 3;

    let mut x: [Box<u32>; SIZE];

    ////x = [Box::new(1), Box::new(2), Box::new(3)];
    //let y = [Box::new(2), Box::new(3), Box::new(4)];
    ////x = y;

    unsafe {
        // 欺騙Rust說x已經被初始化
        x = mem::uninitialized();
        //x = y;//error,呼叫解構函式,再覆蓋,但是x其實沒有真正初始化

        for i in 0..SIZE {
            ptr::write(&mut x[i], Box::new(i as u32));
        }
    }

    println!("{:?}", x);
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
令狐一衝

相關文章