有人告诉我,当move在闭包中使用关键字时,闭包将获取其环境中变量副本的所有权。但这段代码不会编译:
use std::thread;
use std::time::Duration;
fn main() {
let mut data = vec![1, 2, 3];
for i in 0..3 {
thread::spawn(move || {
data[i] += 1;
});
}
thread::sleep(Duration::from_millis(50));
}
Run Code Online (Sandbox Code Playgroud)
错误信息是
use std::thread;
use std::time::Duration;
fn main() {
let mut data = vec![1, 2, 3];
for i in 0..3 {
thread::spawn(move || {
data[i] += 1;
});
}
thread::sleep(Duration::from_millis(50));
}
Run Code Online (Sandbox Code Playgroud)
对此的一些解释
Rust 知道这不安全!如果我们在每个线程中都有一个对数据的引用,并且该线程拥有该引用的所有权,那么我们将拥有三个所有者!
这是否意味着复制仅复制堆栈上的元数据而不是堆上的真实数据?换句话说,复制引用是因为数据类型本身本质上是引用类型,而不是指通过&mut语法创建的引用。move关键字在堆栈上复制数据是否适用于所有数据类型?因此,当涉及到类似类型时,i32它会按值复制,如果是像向量这样的类型,则按引用复制。
我的初衷是了解move关键字的确切行为。仔细查看 Rust 文档后,我认为它遵循一般变量绑定的移动语义。在这种情况下,“数据”的所有权只能转移一次。尽管更改0..3为0..1并没有什么区别。此外,复制的是堆栈上的元数据,而不是堆数据。
这是否意味着复制仅复制堆栈上的元数据而不是堆上的真实数据?换句话说,复制引用是因为数据类型本身本质上是引用类型,而不是引用 &mut 语法创建的引用。move 关键字在堆栈上复制数据是否适用于所有数据类型?因此,当涉及到像 i32 这样的类型时,它会按值复制,如果是像向量这样的类型,则按引用复制。
当变量的所有权转移时,不会执行深复制。所有指针仍然指向相同的值,并且堆内存没有被触及。所有权转让的设计成本低廉。如果您需要深层复制,您可以显式clone调用Vec. 克隆成本低廉的某些类型(例如i32)实现了该Copy特征,这意味着clone如果您尝试将相同值的所有权传递到多个目的地,则会自动调用该特征。
我的初衷是了解 move 关键字的确切行为。仔细查看 Rust 文档后,我认为它遵循一般变量绑定的移动语义。在这种情况下,“数据”的所有权只能转移一次。尽管将 0..3 更改为 0..1 并没有什么区别。此外,复制的是堆栈上的元数据,而不是堆数据。
使用0..1不起作用,因为编译器不会检查可迭代范围0..1是否仅包含单个元素。因此 using0..1不会编译,但完全删除for循环在逻辑上是等效的并且可以编译
use std::thread;
use std::time::Duration;
fn main() {
let mut data = vec![1, 2, 3];
thread::spawn(move || {
data[0] += 1;
});
thread::sleep(Duration::from_millis(50));
}
Run Code Online (Sandbox Code Playgroud)
data如果我们在将其传递给 后再次尝试访问thread::spawn,我们将收到类似 的编译错误error: use of moved value: 'data'。这就是move关键字所做的。由于data已移至闭包,该闭包现在负责释放其内存。还值得注意的是,如果没有关键字,此代码将无法编译move,因为在这种情况下,data将在函数末尾释放main,但线程可能比函数寿命更长main。