Rust 使用'move'关键字的闭包如何创建FnMut闭包?

Rust 使用'move'关键字的闭包如何创建FnMut闭包?,rust,Rust,到目前为止,我认为move |…{…}将在闭包内移动变量,闭包将只实现FnOnce,因为您只能移动变量一次。然而,令我惊讶的是,我发现这段代码是有效的: extern crate futures; use futures::stream; use futures::stream::{Stream, StreamExt}; use std::rc::Rc; #[derive(Debug)] struct Foo(i32); fn bar(r: Rc<Foo>) -> Box

到目前为止,我认为
move |…{…}
将在闭包内移动变量,闭包将只实现
FnOnce
,因为您只能移动变量一次。然而,令我惊讶的是,我发现这段代码是有效的:

extern crate futures;

use futures::stream;
use futures::stream::{Stream, StreamExt};
use std::rc::Rc;

#[derive(Debug)]
struct Foo(i32);

fn bar(r: Rc<Foo>) -> Box<Stream<Item = (), Error = ()> + 'static> {
    Box::new(stream::repeat::<_, ()>(()).map(move |_| {
        println!("{:?}", r);
    }))
}

fn main() {
    let r = Rc::new(Foo(0));
    let _ = bar(r);
}
令我惊讶的是,在使用
move
关键字时创建了
FnMut
闭包,它甚至有
的静态
生存期。在哪里可以找到有关移动的详细信息?或者它实际上是如何工作的

到目前为止,我认为
move |…{…}
将在闭包内移动变量,闭包将只实现
FnOnce
,因为您只能移动变量一次


变量在创建闭包时移动,而不是在调用闭包时移动。由于您只创建了一个闭包,所以移动只会发生一次—不管
map
调用函数的频率有多高。

是的,这一点非常令人困惑,我认为Rust book的措词是有帮助的。读过之后,我的想法和你的一样:一个
move
闭包必然是
FnOnce
,一个非
move
闭包是
FnMut
(也可能是
Fn
)。但这与实际情况有点背道而驰

闭包可以从创建它的范围中捕获值<代码>移动控制这些值进入闭包的方式:通过移动或引用。但它们被捕获后的使用方式决定了闭包是否为
FnMut

如果闭包的主体使用它捕获的任何值,那么闭包只能被
fn一次
。在闭包第一次运行并使用该值后,它不能再次运行

正如您所提到的,您可以通过对闭包调用
drop
或以其他方式使用闭包内的值,但最常见的情况是从闭包返回值,从而将其移出闭包。以下是最简单的示例:

let s = String::from("hello world");
let my_fnonce = move || { s };
如果闭包的主体没有使用任何捕获,那么无论它是否是
move
,它都是
FnMut
。如果它也没有突变任何捕获,那么它也是
Fn
;任何
Fn
的闭包也是
FnMut
。这里有一个简单的例子,虽然不是很好

let s = "hello world";
let my_fn = move || { s.len() }
总结
move
修改器控制创建闭包时如何将捕获移动到闭包中
FnMut
成员身份取决于在执行捕获时如何将捕获移出
闭包(或以其他方式使用)相关书籍章节:这里的重要区别是,
move
导致变量在创建时移动到闭包中,这不会阻止多次调用闭包。如果闭包代码将任何值移出捕获的变量,即使用这些值,则闭包将变成
FnOnce
。如果捕获的变量首先被移动到闭包中,则只能将其移出,但是仅仅将值移入闭包本身并不会使闭包
FnOnce
。我明白了,但是这个主题仍然给我留下了很多困惑。示例-在TRPL中:FnOnce使用它从其封闭范围(称为闭包环境)捕获的变量。要使用捕获的变量,闭包必须拥有这些变量的所有权,并在定义闭包时将它们移动到闭包中。名称的Once部分表示闭包不能多次拥有相同变量的所有权,因此只能调用一次-也就是说,离开v-实施Fn或FnMut都是非法的。但是这段代码运行得非常好:`let r=Some(1);设c:Box=Box::new(move |{drop(r);});c();c()`@AlexeyKarasev关于你的第一个评论,这与这个答案完全一致——另请参见我对这个问题的评论。关于您的第二条评论,您给出的示例有点误导,因为
选项
实现了
复制
特性,因此它实际上没有被
删除()。如果您尝试使用非
Copy
,您将看到该代码的错误。哦,我明白了,所以看起来我对措辞的理解是个问题。所以在TRPL中,消费意味着smth,比如让一些(x)=r在闭包内。如果某个东西在闭包内移动(如本例中的r),他们将其称为捕获。所以基本上FnOnce的意思是,您正在破坏环境,这就是为什么您只能调用它一次(下一次调用将不会有相同的环境)。另一方面,Move处理的是对堆栈的引用,所以它更像是生命周期问题。这是一个更完整的答案。我将不得不把它包括在报告中
let s = "hello world";
let my_fn = move || { s.len() }