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



直到这一刻,我认为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);
}

尽管map有这个签名:

fn map<U, F>(self, f: F) -> Map<Self, F>
where
F: FnMut(Self::Item) -> U, 

令我惊讶的是,在使用move关键字时创建了一个FnMut闭包,它甚至具有'static生命周期。我在哪里可以找到有关move的一些详细信息?或者它实际上是如何工作的?

是的,这一点非常令人困惑,我认为 Rust 书的措辞有所贡献。读完之后,我的想法和你一样:move闭包必然是FnOnce的,非move闭包是FnMut的(也可能是Fn(。但这与实际情况有点倒退。

闭包可以从创建它的作用域捕获值。move控制这些值如何进入闭包:通过移动或引用。但是,捕获它们的使用方式决定了闭包是否FnMut

如果闭包的主体消耗了它捕获的任何值,则闭包只能FnOnce。关闭首次运行并使用该值后,它无法再次运行。

正如你提到的,你可以通过在闭包上调用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,因为您只能移动一次变量。

变量在创建闭包时移动,而不是在调用闭包时移动。由于您只创建一个闭包,因此移动只发生一次 - 无论map调用函数的频率如何。

最新更新