Rust語言內存管理之妙

首發於個人rust 學習日記git

規則

  • 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 的內存引用圖)

  • 圖1-2(s1和s2的引用圖(錯誤)其餘語言js\go 是這樣的)

  • 圖1-3(s1和s2的引用圖(正確)rust 是這樣的,s1賦值s2後,Rust 則認爲 s1 再也不有效)

當值(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

棧類型變量無移動的說法(純拷貝)

  • 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 移出做用域
複製代碼
相關文章
相關標籤/搜索