如何从`FnMut`闭包中返回捕获的变量,这是同时捕获的 [英] How to return the captured variable from `FnMut` closure, which is a captor at the same time

查看:89
本文介绍了如何从`FnMut`闭包中返回捕获的变量,这是同时捕获的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数, collect_n ,它返回一个 Future ,该函数反复地投票 a futures :: sync :: mpsc :: Receiver 并将结果收集到一个向量中,并使用该向量进行解析。问题在于它消耗了 Receiver ,因此无法再次使用 Receiver 。我正在尝试编写一个版本,该版本获取接收器的所有权,然后在返回的 Future 解决。

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 Receivers with a dropped Sender.

另请参见:

  • https://docs.rs/futures/0.3.4/futures/future/trait.FutureExt.html#method.fuse

这篇关于如何从`FnMut`闭包中返回捕获的变量,这是同时捕获的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆