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