我是 Rust 的新手,看起来我在这里严重遗漏了一些概念。
use std::thread;
fn main() {
let mut children = vec![];
//spawn threads
for i in 0..10 {
let c = thread::spawn(|| {
println!("thread id is {}", i);
});
children.push(c);
}
for j in children {
j.join().expect("thread joining issue");
}
}
Run Code Online (Sandbox Code Playgroud)
它失败并出现错误:
error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function
Run Code Online (Sandbox Code Playgroud)
由于i
is的类型i32
,并且不涉及引用,因此 Rust 不应该复制该值而不是强制为move
?
你原来的问题的答案是println!
借用它的论点。但是,正如您在评论中指出的那样,即使(显然)将整数移动到闭包中仍然会导致编译错误。
出于此答案的目的,我们将使用此代码。
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(|| {
let _y = x;
});
}
Run Code Online (Sandbox Code Playgroud)
use_closure
模拟thread::spawn
原始代码中的操作:它使用一个类型必须为 的闭包'static
。
尝试编译这个会产生错误
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(|| {
let _y = x;
});
}
Run Code Online (Sandbox Code Playgroud)
等等,什么?
error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
--> src/main.rs:5:17
|
5 | use_closure(|| {
| ^^ may outlive borrowed value `x`
6 | let _y = x;
| - `x` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:5:5
|
5 | / use_closure(|| {
6 | | let _y = x;
7 | | });
| |______^
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
5 | use_closure(move || {
| ^^^^^^^
Run Code Online (Sandbox Code Playgroud)
为什么在x
那里借?不应该是副本吗?答案在于闭包的“捕获模式”。从文档
编译器更喜欢通过不可变借用,然后是唯一的不可变借用(见下文),可变借用,最后是移动来捕获封闭变量。它将选择允许闭包编译的第一个选项。仅根据闭包表达式的内容进行选择;编译器不会考虑周围的代码,例如相关变量的生命周期。
正是因为x
有一个Copy
类型,闭包本身可以通过一个不可变的借用来编译。给定一个不可变的借用x
(称为bor
),我们可以对_y
with进行赋值_y = *bor
。这不是“移出引用后面的数据”,因为这是复制而不是移动。
然而,由于闭包借用了一个局部变量,它的类型不会是'static
,所以它不能在use_closure
or 中使用thread::spawn
。
尝试使用 not 类型的相同代码Copy
,它实际上工作得很好,因为闭包必须x
通过移动它来捕获。
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: Vec<i32> = vec![];
use_closure(|| {
let _y = x;
});
}
Run Code Online (Sandbox Code Playgroud)
当然,正如您已经知道的,解决方案是move
在闭包前使用关键字。这会强制所有捕获的变量移动到闭包中。由于不会借用变量,因此闭包将具有静态类型,并且可以在use_closure
或 中使用thread::spawn
。
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(move || {
let _y = x;
});
}
Run Code Online (Sandbox Code Playgroud)