为什么 Rust 在生成线程时强制在 i32 情况下使用 move?

2024-04-27

我是 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");
    }
}

它失败并出现错误:

error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function

由于类型为i is i32,并且不涉及任何引用,Rust 不应该复制值而不是强制move ?


你原来问题的答案是println!借用其论据。 https://stackoverflow.com/questions/30450399/does-println-borrow-or-own-the-variable但是,正如您在评论中指出的那样,即使(显然)将整数移动到闭包中仍然会导致编译错误。

出于本答案的目的,我们将使用此代码。

fn use_closure<F: FnOnce() + 'static>(_: F) {}

fn main() {
    let x: i32 = 0;
    use_closure(|| {
        let _y = x;
    });
}

use_closure模拟什么thread::spawn原始代码中的作用:它消耗一个类型必须是的闭包'static.

尝试编译此错误

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 || {
  |                 ^^^^^^^

等等,什么?

6 |         let _y = x;
  |                  - `x` is borrowed here

Why is x那里借的?不应该是复印件吗?答案在于闭包的“捕获模式”。从文档 https://doc.rust-lang.org/reference/types/closure.html#capture-modes

编译器更喜欢通过不可变借用捕获封闭变量,然后是唯一的不可变借用(见下文),通过可变借用,最后通过移动。它将选择允许闭包编译的第一个选择。该选择仅针对闭包表达式的内容进行;编译器不考虑周围的代码,例如所涉及变量的生命周期。

正是因为x has a Copy类型,闭包本身可以通过不可变的借用进行编译。给定一个不可变的借用x(叫它bor),我们可以做我们的任务_y with _y = *bor。这不是“移出引用后面的数据”,因为这是副本而不是移动。

然而,由于闭包借用了一个局部变量,它的类型不会是'static,所以它不能用于use_closure or thread::spawn.

使用不同的类型尝试相同的代码Copy,它实际上工作得很好,因为闭包被强制捕获x通过移动它。

fn use_closure<F: FnOnce() + 'static>(_: F) {}

fn main() {
    let x: Vec<i32> = vec![];
    use_closure(|| {
        let _y = x;
    });
}

当然,正如您所知,解决方案是使用move闭包前面的关键字。这会强制将所有捕获的变量移至闭包中。由于变量不会被借用,因此闭包将具有静态类型并且能够在use_closure or thread::spawn.

fn use_closure<F: FnOnce() + 'static>(_: F) {}

fn main() {
    let x: i32 = 0;
    use_closure(move || {
        let _y = x;
    });
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Rust 在生成线程时强制在 i32 情况下使用 move? 的相关文章

随机推荐