Rust 中的 move 语义
Nilesh Katuwal
2022年6月7日
在本文中,我们将了解 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