直到这一刻,我认为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
调用函数的频率如何。