Rust 中的 move 語義

Nilesh Katuwal 2022年6月7日
Rust 中的 move 語義

在本文中,我們將瞭解 Rust 中的移動語義。

在 Rust 中移動語義

在 Rust 中,所有型別都是可變的,所有移動操作都相當於原始資料在新位置的位副本。Move 通過引用或可變引用將任何捕獲的變數轉換為按值捕獲的變數。

當涉及執行緒時,通常使用移動。

Rust 過程也具有破壞性。例如,離開變數後,該變數在程式碼中不再可用。

在 Rust 中,移動始終通過複製物件本身的資料而不破壞原始物件來實現。它永遠不會通過複製物件的託管資源或執行自定義程式碼來實現。

Rust 對不需要移動語義的型別例外。相反,該值包含表示它的所有必要資訊,並且沒有堆分配或資源由該值管理,例如 i32

這些型別實現了特殊的 Copy 特徵,因為複製成本低,並且是傳遞給函式或管理分配的預設方法:

fn foo(bar: i32) {
    // Implementation
}

let var: i32 = 9;
foo(var); // copy
foo(var); // copy
foo(var); // copy

預設函式呼叫對 Copy 以外的型別(例如 String)使用移動語義。在 Rust 中,當一個變數被移動時,它的生命週期會提前結束。

在編譯時,move 替換了塊末尾的解構函式呼叫,因此為 String 編寫如下程式碼是錯誤的:

fn foo(bar: String) {
    // Implementation
}

let var: String = "Hello".to_string();
foo(var); // Move
foo(var); // Compile-Time Error
foo(var); // Compile-Time Error

克隆 rust 需要實現,但對於所有移動都是相同的:複製值中的記憶體並且不呼叫原始值的解構函式。

而在 Rust 中,通過這種精確的實現,所有型別都是可移動的;不可移動型別不存在(儘管不可移動值存在)。位元組必須在新位置編碼有關該值管理的資源的資訊,例如指標,就像它們在先前位置中所做的一樣有效。

Rust 有一種稱為 pinning 的機制,它在型別系統中指示特定值永遠不會再次移動,它可用於實現自引用值並在非同步中使用。

例子:

fn destroy_box(z: Box<i32>) {
    println!("The box that contains {} is destroyed", z);
}

fn main() {
    let a = 5u32;
    let b = a;
    println!("a is {}, and y is {}", a, b);

    let m = Box::new(5i32);

    println!("a contains: {}", m);
    let n = m;
    destroy_box(n);
}

destroy_box 函式獲取上述原始碼中堆分配記憶體的所有權。首先,z 被銷燬,記憶體被釋放。

m 是指向堆分配整數的指標。

輸出:

a is 5, and y is 5
a contains: 5
The box that contains 5 is destroyed