无法移出 `FnMut` 闭包中捕获的变量 [英] Cannot move out of captured variables in an `FnMut` closure
问题描述
pub fn create_future(通知器:mpsc::Sender,代理:代理,) ->impl Future- {proxy.something().and_then(移动|子| {sub.for_each(move |a| {//<---- 关闭 Aproxy.something_else(a).and_then(move |b| {//<---- 关闭 Bnotificator.send(b.len());//<---- 错误!好的(())}).or_else(|e| {恐慌!(哎呀{}",e);好的(())})})}).map_err(|e| {()})}
这不会编译,因为
.and_then(move |b| {^^^^^^^^^ 无法移出`FnMut` 闭包中捕获的外部变量
我对错误的理解是:
- 闭包 B 是
FnMut
,它通过获取其所有权来捕获notificator
- 在闭包 B 中,
send
再次需要取得所有权 - 现在
send
和 Closure B 都在修改notificator
因此错误.
我的理解对吗?我该如何解决这个问题?
嵌套闭包很棘手.
考虑一下:
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) {println!("字节数:{:?}", f());}fn 主(){use_a_fn_multiple_times(|a: 字符串| {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) {println!("字节数:{:?}", f());}fn 主(){让outer_s = "见:".to_owned();use_a_fn_multiple_times(|a: 字符串| {use_fn_once(移动 || {让 mut v = outer_s.into_bytes();v.extend(a.into_bytes());v});});}
游乐场>
然后你会得到你看到的错误(除了 Fn
和 FnMut
,这对问题来说无关紧要).每次调用外部闭包时都会重新创建内部闭包(它必须如此,因为它每次都必须捕获 a
),但它尝试通过以下方式捕获 outer_s
每次移动.这是行不通的;在第一次之后,outer_s
被移出并因此无效.
要将其映射回您的代码,说闭包 B 捕获 notificator
"是错误的,因为闭包 B 不只一个.有必要的数量,但通常您嵌套的 and_then
和 for_each
调用将在这段代码中结束.但只有一个人可以通过移动捕捉.
所以要解决这个问题,你要么需要确保只有一个闭包 B,要么确保你有足够的 mpsc::Sender
供所有人使用.
第一种方法是从嵌套上下文中拉出闭包.
let closure_b = 移动 |b|{notificator.send(b.len());好的(())};proxy.something().and_then(移动|子| {sub.for_each(move |a| {//<---- 关闭 Aproxy.something_else(a).and_then(closure_b).or_else(|e| {恐慌!(哎呀{}",e);好的(())})})}).map_err(|e| {()})
除此之外不起作用,因为现在Closure A面临同样的问题,所以你必须多次这样做:
let closure_b = 移动 |b|{notificator.send(b.len());好的(())};让closure_a = 移动|a|{proxy.something_else(a).and_then(closure_b).or_else(|e| {恐慌!(哎呀{}",e);好的(())})};proxy.something().and_then(移动|子| {sub.for_each(closure_a)}).map_err(|e| {()})
第二种方式涉及很多 clone()
调用,而且由于我无法对您的代码进行类型检查,因此我不会尝试编写它.
当一切都完成后,您的代码仍然会失败,因为您在尝试使用 Proxy
的同时还尝试使用它.
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| {
()
})
}
This doesn't compile because
.and_then(move |b| {
^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure
My understanding of the error is:
- Closure B is
FnMut
, and it capturesnotificator
by taking its ownership - In Closure B,
send
again needs to take the ownership - Now both
send
and Closure B are modifyingnotificator
thus the error.
Is my understanding right? How can I solve this problem?
Nested closures are tricky.
Consider this:
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());
});
}
Notice that the inner closure captures a
by move. This is fine. The outer closure owns a
and can do with it what it wants, including moving it into the inner closure (which, because it consumes its captured value, is a FnOnce
).
The outer closure is called multiple times, each time with a new string, and every time a new inner closure capturing this string is created.
But what if what you want to capture comes from even further out?
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
});
});
}
Then you get the error you're seeing (except for Fn
vs FnMut
, which is immaterial to the problem). The inner closure is created anew on every call to the outer closure (it has to be, because it has to capture a
every time), but it tries to capture outer_s
by move every time. This can't work; after the first time, outer_s
is moved from and thus invalid.
To map this back to your code, it's wrong to say "Closure B captures notificator
", because there isn't just one Closure B. There's as many as necessary, however often your nested and_then
and for_each
calls will end up in that piece of code. But only one can ever capture by move.
So to solve this, you either need to make sure there's only one Closure B, or make sure you have enough mpsc::Sender
s for everyone.
The first way is done by pulling the closure out of the nested context.
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| {
()
})
except that won't work, since now Closure A faces the same issue, so you have to do it multiple times:
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| {
()
})
The second way involves a lot of clone()
calls, and since I can't type-check your code, I won't attempt to write it.
When all is said and done, though, your code will still fail, because you're moving out of Proxy
while also trying to use it.
这篇关于无法移出 `FnMut` 闭包中捕获的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!