为什么将值移动到闭包中仍然有错误消息“无法将不可变的局部变量借用为可变的”?

Mic*_*man 5 rust

在下面的代码中,我明确地强制将namefrommain函数移动到闭包中,并且一切正常:

fn main() {
    let name = String::from("Alice");

    let welcome = || {
        let mut name = name;
        name += " and Bob";
        println!("Welcome, {}", name);
    };

    welcome();
}
Run Code Online (Sandbox Code Playgroud)

我原以为move在闭包的开头添加 a会完成同样的事情,并导致值被移动并创建 a FnOnce

fn main() {
    let name = String::from("Alice");

    let welcome = move || {
        name += " and Bob";
        println!("Welcome, {}", name);
    };

    welcome();
}
Run Code Online (Sandbox Code Playgroud)

但是,我收到了错误消息:

fn main() {
    let name = String::from("Alice");

    let welcome = || {
        let mut name = name;
        name += " and Bob";
        println!("Welcome, {}", name);
    };

    welcome();
}
Run Code Online (Sandbox Code Playgroud)

move在这种情况下,考虑闭包的正确方法是什么?

Sve*_*ach 6

我原以为move在闭包的开头添加 a会完成同样的事情,......

它做同样的事情。您只是忘记声明namewelcome为可变的。这段代码工作正常:

fn main() {
    let mut name = String::from("Alice");

    let mut welcome = move || {
        name += " and Bob";
        println!("Welcome, {}", name);
    };

    welcome();
}
Run Code Online (Sandbox Code Playgroud)

两个版本的闭包都name被移到了闭包中。在第一个版本中,这是由name闭包内部的消费隐式引起的。第二个版本不消耗name,而是使用move关键字来强制移动。

...并导致值被移动并创建一个FnOnce.

将值移动到闭包中并不能使它成为FnOnce。如果一个闭包消耗了一个捕获的值,它就会变成FnOnce,因为它显然只能这样做一次。因此,闭包的第一个版本是FnOnce,因为它消耗name。上面的 clousre 是FnMut,并且可以被多次调用。调用它两次导致输出

fn main() {
    let mut name = String::from("Alice");

    let mut welcome = move || {
        name += " and Bob";
        println!("Welcome, {}", name);
    };

    welcome();
}
Run Code Online (Sandbox Code Playgroud)

(我上面有点草率地使用了函数 trait 名称。实际上,每个闭包都实现了FnOnce,因为每个闭包至少可以被调用一次。有些闭包可以被多次调用,所以它们是FnMut额外的。还有一些可以被多次调用的闭包时间不会改变他们被捕获的状态,所以他们是Fn对其他两个特征的补充。)

  • 尝试在“welcome”之后打印“name”变量。由于“move”关键字,它将导致编译时错误。您可以多次调用“welcome”,因为它是“FnMut”并且它捕获“name”变量。[此](https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=f1bb59a9dae591b95c5c291eb38d0ab0)来自[rust示例](https://doc.rust-lang.org/rust- by-example/fn/closures/input_parameters.html)可以澄清它 (2认同)
  • @MichaelSnoyman `move` _does_ 强制将 `name` 移动到闭包中。第一个版本还将值移动到闭包中,但然后在闭包内消耗它。消耗捕获值的闭包只能被调用一次。 (2认同)