闭包是“FnOnce”,因为它将变量“________”移出其环境

ser*_*ter 9 rust

我在将引用计数变量移动到需要实现的闭包中时遇到了一些问题FnMut

下面的代码工作正常:

use std::rc::Rc;

fn main() {
    let callback;

    let data: Rc<Vec<f32>> = Rc::new(Vec::new());

    {
        let data = data.clone();

        callback = move || {
            data.iter().for_each(|v| println!("{}", v));
        };

        consumer(callback);
    }
}

fn consumer<D>(callback: D)
where
    D: FnMut(),
{
}
Run Code Online (Sandbox Code Playgroud)

如果我将闭包更改为:

callback = move || {
    data;
};
Run Code Online (Sandbox Code Playgroud)

编译器产生错误:

use std::rc::Rc;

fn main() {
    let callback;

    let data: Rc<Vec<f32>> = Rc::new(Vec::new());

    {
        let data = data.clone();

        callback = move || {
            data.iter().for_each(|v| println!("{}", v));
        };

        consumer(callback);
    }
}

fn consumer<D>(callback: D)
where
    D: FnMut(),
{
}
Run Code Online (Sandbox Code Playgroud)

这个例子有效:

callback = move || {
    let x = data[0];
    println!("{}", x);
};
Run Code Online (Sandbox Code Playgroud)

这个例子没有:

callback = move || {
    let x = data;
    println!("{}", x[0]);
};
Run Code Online (Sandbox Code Playgroud)

使用let data: Arc<Mutex<Vec<f32>>> = Arc::new(Mutex::new(Vec::new()));,这有效:

callback = move || {
    let x = data.lock().unwrap();
};
Run Code Online (Sandbox Code Playgroud)

这不会:

callback = move || {
    let x = data;
};
Run Code Online (Sandbox Code Playgroud)

这背后的原因是什么?

Kev*_*eid 7

\n

下面的代码工作正常:

\n
let data: Rc<Vec<f32>> = Rc::new(Vec::new());\n{\n    let data = data.clone();\n    callback = move || {\n        data.iter().for_each(|v| println!("{}", v));\n    };\n
Run Code Online (Sandbox Code Playgroud)\n
\n

首先,当构造闭包时,data移入其中,因为闭包被声明为move ||。这就是为什么如果你想data在闭包之外再次使用你需要事先克隆

\n

然后,当调用闭包时, iter()是一个采用 的方法&self,因此data不会被方法调用移出;它是借来的,并且闭包仍然拥有它。因此,可以根据需要多次调用闭包 \xe2\x80\x94 它会自动实现Fn,而不仅仅是FnOnce.

\n
\n
callback = move || {\n    data;\n};\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n
callback = move || {\n    let x = data;\n    println!("{}", x[0]);\n};\n
Run Code Online (Sandbox Code Playgroud)\n
\n

在这里,当闭包被调用时,data被移动(通过值使用,而不是通过引用)。第一种情况,立即丢弃;在第二个中,它被移动到局部变量中x

\n

如果将一个值移出闭包,则该闭包仅实现FnOnce,并且只能被调用一次,因为如果第二次调用该闭包,则该值将不再可用。

\n

一般来说,任何简单提及变量名都是一种举动。其他一切都是特定的例外:

\n
    \n
  • 如果该值实现了该Copy特征,那么它可以是副本而不是移动,这意味着原始值保持有效。(此处不适用,因为Rc未实现Copy。)
  • \n
  • .iter()如果您调用诸如or之类的方法.lock(),则该方法可以采用&selfor &mut self;Rust 自动获取引用,就好像您已经编写过一样(&data).iter()一样。
  • \n
  • println!是一个宏,为了方便起见,它隐式引用其参数。
  • \n
\n

如果你写&data;let x = &data;,那么您将创建一个引用,该引用不会移动原始值。

\n