pub fn create_future(
notificator: mpsc::Sender<usize>,
proxy: Proxy,
) -> impl Future<Item = (), Error = ()> {
proxy.something()
.and_then(move |sub| {
sub.for_each(move |a| { // <---- Closure A
proxy.something_else(a)
.and_then(move |b| { // <---- Closure B
notificator.send(b.len()); // <---- Error!
Ok(())
})
.or_else(|e| {
panic!("oops {}", e);
Ok(())
})
})
})
.map_err(|e| {
()
})
}
这不会编译,因为
.and_then(move |b| {
^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure
我对错误的理解是:
FnMut
,它通过获取所有权notificator
send
再次需要拥有所有权send
和闭包B都在修改notificator
,因此出现了错误。我的理解正确吗?我该如何解决这个问题?
答案 0 :(得分:3)
嵌套闭包很棘手。
考虑一下:
fn use_a_fn_multiple_times(f: impl Fn(String)) {
f("foo".to_owned());
f("bar".to_owned());
}
fn use_fn_once(f: impl FnOnce() -> Vec<u8>) {
println!("Bytes: {:?}", f());
}
fn main() {
use_a_fn_multiple_times(|a: String| {
use_fn_once(move || a.into_bytes());
});
}
请注意,内部闭包通过移动捕获了a
。这可以。外部闭包拥有a
并可以执行其所需的操作,包括将其移入内部闭包(由于它消耗了捕获的值,因此是FnOnce
)。
多次调用外部闭包,每次使用新字符串,并且每次创建捕获该字符串的新内部闭包。
但是,如果要捕获的内容来自更远的地方怎么办?
fn use_a_fn_multiple_times(f: impl Fn(String)) {
f("foo".to_owned());
f("bar".to_owned());
}
fn use_fn_once(f: impl FnOnce() -> Vec<u8>) {
println!("Bytes: {:?}", f());
}
fn main() {
let outer_s = "see:".to_owned();
use_a_fn_multiple_times(|a: String| {
use_fn_once(move || {
let mut v = outer_s.into_bytes();
v.extend(a.into_bytes());
v
});
});
}
然后您将看到错误(Fn
与FnMut
除外,这对问题无关紧要)。内部闭包是在每次调用外部闭包时重新创建的(必须如此,因为它每次必须捕获a
,但每次尝试通过移动来捕获outer_s
。这行不通;第一次之后,outer_s
被移出并因此无效。
要将其映射回您的代码,请说“ closure B捕获notificator
”是错误的,因为不仅有一个ClosureB。有很多需要,但是经常嵌套{{1} }和and_then
调用将最终出现在该段代码中。但是只有一个人可以通过移动捕获。
因此,要解决此问题,您需要确保只有一个封闭B,或者确保每个人都有足够的for_each
。
第一种方法是将闭包从嵌套上下文中拉出。
mpsc::Sender
除非这是行不通的,否则从现在开始,闭包A面临相同的问题,因此您必须执行多次:
let closure_b = move |b| {
notificator.send(b.len());
Ok(())
};
proxy.something()
.and_then(move |sub| {
sub.for_each(move |a| { // <---- Closure A
proxy.something_else(a)
.and_then(closure_b)
.or_else(|e| {
panic!("oops {}", e);
Ok(())
})
})
})
.map_err(|e| {
()
})
第二种方法涉及许多let closure_b = move |b| {
notificator.send(b.len());
Ok(())
};
let closure_a = move |a| {
proxy.something_else(a)
.and_then(closure_b)
.or_else(|e| {
panic!("oops {}", e);
Ok(())
})
};
proxy.something()
.and_then(move |sub| {
sub.for_each(closure_a)
})
.map_err(|e| {
()
})
调用,并且由于我无法类型检查您的代码,因此我不会尝试编写它。
尽管一切都说完了,但是您的代码仍然会失败,因为您要移出clone()
,同时还要尝试使用它。