Rust語言記憶體管理之妙

_西門吹牛發表於2018-12-23

首發於我的rust 學習日記

規則

  • Rust 中的每一個值都有一個被稱為其 所有者(owner)的變數。
  • 值有且只有一個所有者。
  • 當所有者(變數)離開作用域,這個值將被丟棄。(Rust 在結尾的
    }處自動呼叫 drop釋放記憶體)
  • 移動(堆記憶體變數),當值(s1)被賦值給另外一個變數(s2)後,rust則認為變數s1,不再有效。
  • 棧型別變數無移動的說法(沒有深淺拷貝的區別)
  • 將值傳遞給函式在語義上與給變數賦值相似

變數與資料的互動(move)

  • 將 5 繫結到 x;接著生成一個值 x 的拷貝並繫結到 y。現在有了兩個變數,x 和 y,都等於 5。因為正數是有已知固定大小的簡單值,所以這兩個 5 被放入了棧中(重點:兩個值都放入棧中了)。
    let x = 5;
    let y = x;
    複製程式碼
  • s1 和 s2 這兩個變數指向相同的地址(hello分配的堆記憶體)。
    let s1 = String::from("hello");
    let s2 = s1;
    複製程式碼
  • 圖1-1(s1 的記憶體引用圖)
Rust語言記憶體管理之妙
  • 圖1-2(s1和s2的引用圖(錯誤)其他語言js\go 是這樣的)
Rust語言記憶體管理之妙
  • 圖1-3(s1和s2的引用圖(正確)rust 是這樣的,s1賦值s2後,Rust 則認為 s1 不再有效)
Rust語言記憶體管理之妙

當值(s1)被賦值給另外一個變數(s2)後,rust則認為變數s1,不再有效(圖1-3)

  • Rust 在結尾的
    }(作用域結束後) 處自動呼叫 drop(釋放記憶體)。
  • 如上圖,當 s2 和 s1 離開作用域,他們都會嘗試釋放相同的記憶體。這是一個叫做 二次釋放(是錯誤的)。
  • 為了確保記憶體安全,Rust 則認為 s1 不再有效,因此 Rust 不需要在 s1 離開作用域後清理任何東西,如下案例。
    fn main() { 
    let s1 = String::from("hello");
    let s2 = s1;
    // 本行會報錯 value borrowed here after move println!("{
    }, world!"
    , s1);

    }複製程式碼

克隆(深拷貝)

  • 實現如下圖,賦值變數的同時,進行資料拷貝的方案,rust也是支援的
  • 這段程式碼能正常執行,產生的變數記憶體如圖1-4
    fn main() { 
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {
    }, s2 = {
    }"
    , s1, s2);

    } 複製程式碼
  • 圖1-4
Rust語言記憶體管理之妙

棧型別變數無移動的說法(純拷貝)

  • x,y是編譯時確定大小的型別,因此整個儲存在棧上,所以拷貝其實際的值是快速的。因此rust對棧變數進行純拷貝,便不會造成效能的影響。
  • 也就意味著建立變數 y 後, 沒必要使x 無效。
let x = 5;
let y = x;
println!("x = {
}, y = {
}"
, x, y);
// x = 5, y = 5複製程式碼

將值傳遞給函式在語義上與給變數賦值相似

fn main() { 
let s = String::from("hello");
// s 進入作用域 run_move(s);
// s 的值移動到函式裡,s失效 // 因此到這裡,s不再有效 /* 將會報錯:因為s已經被move 報錯資訊:will error value borrowed here after move println!("s:{
}",s);
*/
let x = 5;
// x 進入作用域 run_copy(x);
// x 應該移動函式裡 println!("x:{
}"
,x);
// 因為 x 是 棧變數,因為不會被 move 使失效
} // x 移出了作用域,// s 移出了作用域但,因為 s 的值已被移走,所以不會有特殊操作fn run_move(some_string: String) {
// some_string 進入作用域 println!("run_move:{
}"
, some_string);

} // 這裡,some_string 移出作用域並呼叫 `drop` 方法。佔用的記憶體被釋放fn run_copy(some_integer: i32) {
// some_integer 進入作用域 println!("run_copy:{
}"
, some_integer);

} // some_integer 移出作用域複製程式碼

來源:https://juejin.im/post/5c1f0242f265da612061d073

相關文章