Rust 闭包是'FnOnce',因为它将变量''.'移出其环境

Rust 闭包是'FnOnce',因为它将变量''.'移出其环境,rust,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.it

我在将引用计数变量移动到闭包中时遇到了一些问题,这些闭包需要实现
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(),
{
}
let data: Rc<Vec<f32>> = Rc::new(Vec::new());
{
    let data = data.clone();
    callback = move || {
        data.iter().for_each(|v| println!("{}", v));
    };
编译器将生成以下错误:

-->src/test1.rs:11:20
|
11 |回调=移动|{
|^^^^^^此闭包实现的是'FnOnce',而不是'FnMut'`
12 |数据;
|----闭包是'FnOnce',因为它将变量'data'移出其环境
...
15 |消费者(回拨);
|-----实现`FnMut`的要求源于此
此示例适用于:

callback = move || {
    let x = data[0];
    println!("{}", x);
};
此示例不包括:

callback = move || {
    let x = data;
    println!("{}", x[0]);
};
callback = move || {
    let x = data;
};
使用
let data:Arc=Arc::new(Mutex::new(Vec::new());
,这可以:

callback = move || {
    let x = data.lock().unwrap();
};
这并不是:

callback = move || {
    let x = data;
    println!("{}", x[0]);
};
callback = move || {
    let x = data;
};
这背后的原因是什么

以下代码可以正常工作:

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(),
{
}
let data: Rc<Vec<f32>> = Rc::new(Vec::new());
{
    let data = data.clone();
    callback = move || {
        data.iter().for_each(|v| println!("{}", v));
    };
在这里,当调用闭包时,
数据
被移动(由值使用,而不是引用)。在第一种情况下,它被立即删除;在第二种情况下,它被移动到局部变量
x

如果从闭包中移出一个值,那么闭包只实现一次
FnOnce
,并且只能调用一次,因为如果第二次调用闭包,它将不再使用该值

一般来说,任何简单提及变量名的行为都是一种移动。其他所有行为都是一个特殊的例外:

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

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

为了从与公认答案稍有不同的角度涵盖同一主题,有两个概念是相关的,但不同:

  • 移动
    确保将
    数据
    值移动到闭包中,并且不会超过闭包

  • 以上内容与是否仅调用闭包需要使用数据无关

  • 如果是
    move | | data.lock().unwrap()
    ,则闭包的调用实现需要
    &self
    ,并且可以多次调用,只有在删除闭包时才会销毁
    数据

    move | | data
    的情况下,闭包的调用实现采用
    self
    并立即消耗数据(以及闭包的其余部分),这就是为什么它只能调用一次,并在返回时丢弃
    数据


    在#2中,使用数据的闭包体(
    move | | data
    或just
    | | data
    )意味着闭包获取捕获的值的所有权。反之则不适用:仅仅是您使用
    move
    强制闭包接管值的所有权并不意味着调用将消耗该值。但是如果您愿意,您可以通过执行类似
    drop(data)的操作来强制执行该操作
    在闭包体中,这将使您的闭包
    fn一次

    为什么要从
    让audio\u buffer=audio\u buffer;
    开始?我认为如果您放下它并正常使用外部的
    audio\u buffer
    就可以了。仅仅放一行
    audio\u buffer;
    会导致问题,这行的唯一目的是我s引用变量进行演示。但是您实际如何使用
    audio\u buffer
    ?如果调用使用外部值的对象,您将得到此错误。如果调用仅使用外部值引用的对象,我相信它会起作用。
    让audio\u buffer=audio\u buffer;
    意味着从out作用域指向一个特定函数调用的作用域,这意味着该函数只能调用一次,否则相同的值将被移动多次。否,即使在
    //某些内容中没有任何内容,也会发生相同的错误。@sergmister OK。您能编辑您的问题以提供一个完整的、可编译的示例吗(最好是在中工作的)?当我们可以看到编译错误并探索更改的影响时,更容易精确。好的,我将这样做“这里,iter()是一个采用&self的方法。因此,数据不会被方法调用移出/消耗。”那么,如果数据未被使用,为什么不能在闭包后使用它呢?@sergmister它被移动到闭包中,因为您编写了
    move |
    。如果没有
    move
    ,它将被借用。我编辑了我的答案,以进一步描述这一点。