如何从`FnMut`闭包中返回捕获的变量,这是同时捕获的 [英] How to return the captured variable from `FnMut` closure, which is a captor at the same time
问题描述
我有一个函数, collect_n
,它返回一个 Future
,该函数反复地投票
a futures :: sync :: mpsc :: Receiver
并将结果收集到一个向量中,并使用该向量进行解析。问题在于它消耗了 Receiver
,因此无法再次使用 Receiver
。我正在尝试编写一个版本,该版本获取接收器
的所有权,然后在返回的 Future $ c $时将所有权返回给调用方c>解决。
I have a function, collect_n
, that returns a Future
that repeatedly polls
a futures::sync::mpsc::Receiver
and collects the results into a vector, and resolves with that vector. The problem is that it consumes the Receiver
, so that Receiver
can't be used again. I'm trying to write a version that takes ownership of the Receiver
but then returns ownership back to the caller when the returned Future
resolves.
这是我写的:
/// Like collect_n but returns the mpsc::Receiver it consumes so it can be reused.
pub fn collect_n_reusable<T>(
mut rx: mpsc::Receiver<T>,
n: usize,
) -> impl Future<Item = (mpsc::Receiver<T>, Vec<T>), Error = ()> {
let mut events = Vec::new();
future::poll_fn(move || {
while events.len() < n {
let e = try_ready!(rx.poll()).unwrap();
events.push(e);
}
let ret = mem::replace(&mut events, Vec::new());
Ok(Async::Ready((rx, ret)))
})
}
这会导致编译错误:
error[E0507]: cannot move out of `rx`, a captured variable in an `FnMut` closure
--> src/test_util.rs:200:26
|
189 | mut rx: mpsc::Receiver<T>,
| ------ captured outer variable
...
200 | Ok(Async::Ready((rx, ret)))
| ^^ move occurs because `rx` has type `futures::sync::mpsc::Receiver<T>`, which does not implement the `Copy` trait
我如何完成我想做的事情?
How can I accomplish what I'm trying to do?
推荐答案
除非 Rn
或 Arc
这样的变量是可共享的,否则这是不可能的,因为 FnMut <可以多次调用/ code>,这可能是您的闭包需要返回一个以上的捕获变量。但是在返回之后,由于安全原因,Rust不允许您返回变量的所有权,因此您无法将其返回。
It is not possible unless your variable is shareable like Rc
or Arc
, since FnMut
can be called multiple times it is possible that your closure needs to return the captured variable more than one. But after returning you lose the ownership of the variable so you cannot return it back, due to safety Rust doesn't let you do this.
根据您的逻辑,我们知道一旦您的 Future
准备就绪,就无需再次对其进行轮询,因此我们可以创建解决方案而无需使用智能指针。让我们考虑如下容器对象,我使用了 Option
:
According to your logic we know that once your Future
is ready it will not need to be polled again, so we can create a solution without using smart pointer. Lets consider a container object like below, I've used Option
:
use futures::sync::mpsc;
use futures::{Future, Async, try_ready};
use futures::stream::Stream;
use core::mem;
pub fn collect_n_reusable<T>(
mut rx: mpsc::Receiver<T>,
n: usize,
) -> impl Future<Item = (mpsc::Receiver<T>, Vec<T>), Error = ()> {
let mut events = Vec::new();
let mut rx = Some(rx); //wrapped with an Option
futures::future::poll_fn(move || {
while events.len() < n {
let e = try_ready!(rx.as_mut().unwrap().poll()).unwrap();//used over an Option
events.push(e);
}
let ret = mem::replace(&mut events, Vec::new());
//We took it from option and returned.
//Careful this will panic if the return line got execute more than once.
Ok(Async::Ready((rx.take().unwrap(), ret)))
})
.fuse()
}
基本上,我们捕获了容器而不是接收器。因此,这使编译器感到高兴。我使用 fuse()
来确保在返回 Async :: Ready
后不会再次调用闭包,否则
Basically we captured the container not the receiver. So this made compiler happy. I used fuse()
to make sure the closure will not be called again after returning Async::Ready
, otherwise it would panic for further polls.
另一个解决方案是使用 std :: mem :: swap
:
The other solution would be using std::mem::swap
:
futures::future::poll_fn(move || {
while events.len() < n {
let e = try_ready!(rx.poll()).unwrap();
events.push(e);
}
let mut place_holder_receiver = futures::sync::mpsc::channel(0).1; //creating object with same type to swap with actual one.
let ret = mem::replace(&mut events, Vec::new());
mem::swap(&mut place_holder_receiver, &mut rx); //Swapping the actual receiver with the placeholder
Ok(Async::Ready((place_holder_receiver, ret))) //so we can return placeholder in here
})
.fuse()
我已经将实际的接收器
与 place_holder_receiver
。由于占位符是在 FnMut
(未捕获)中创建的,因此我们可以根据需要返回任意多次,因此编译器很高兴。感谢 fuse()
在成功返回后将不会调用此闭包,否则它将返回 Receiver
并带有一个发件人
。
Simply I've swapped actual receiver
with the place_holder_receiver
. Since placeholder is created in FnMut
(Not captured) we can return it as many time as we want, so compiler is happy again. Thanks to fuse()
this closure will not be called after successful return, otherwise it would return the Receiver
s with a dropped Sender
.
另请参见:
- https://docs.rs/futures/0.3.4/futures/future/trait.FutureExt.html#method.fuse
这篇关于如何从`FnMut`闭包中返回捕获的变量,这是同时捕获的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!