繁体   English   中英

如何在 Rust 中组成两个异步 function

[英]How to compose two async function in Rust

我正在尝试编写组成两个异步 function 的更高阶 function。

我基本上是寻找异步版本

fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
    F: Fn(A) -> B,
    G: Fn(B) -> C,
{
    move |x| g(f(x))
}

这是我迄今为止的尝试。

fn compose_future<A, B, C, G, F>(f: F, g: G) -> (impl Fn(A) -> impl Future<C>)
where
    F: Fn(A) -> impl Future<B>,
    G: Fn(B) -> impl Future<C>,
{
    move |x| async { g(f(x).await).await }
}

我收到以下错误

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> src\channel.rs:13:17
   |
13 |     F: Fn(A) -> impl Future<B>,
   |                 ^^^^^^^^^^^^^^

有可能做到这一点吗?

我不确定使用impl Trait -s 是否可以做到这么简单。 我能想出的一个解决方案是老式的未来类型使用,没有 async-await 功能。 TLDR: 完整的操场 Async-await 使用了一个生成器,它内部包含一个 state 机器,所以我们需要手动定义它:

enum State<In, F, FutOutF, G, FutOutG> {
    Initial(In, F, G), // Out composed type created
    FirstAwait(FutOutF, G), // Composed type waits for the first future
    SecondAwait(FutOutG), // and for the second
    // here can be a `Completed` state, but it simpler
    // to handle it with `Option<..>` in our future itself
}

然后定义一个组合类型本身:

struct Compose<In, Out, F, FutOutF, G, FutOutG> {
    state: Option<State<In, F, FutOutF, G, FutOutG>>,
    _t: PhantomData<Out>,
}

// And "entry-point" would be something like that:
fn compose_fut<In, Out, F, FutOutF, G, FutOutG>(
    i: In,
    f: F,
    g: G,
) -> Compose<In, Out, F, FutOutF, G, FutOutG> {
    Compose {
        state: Some(State::Initial(i, f, g)),
        _t: PhantomData,
    }
}

然后是最复杂的部分 - impl Future本身,这里是一个没有实现的基本 impl 声明:

impl<In, Mid, Out, F, FutOutF, G, FutOutG> Future for Compose<In, Out, F, FutOutF, G, FutOutG>
where
    FutOutF: Future<Output = Mid>,
    F: FnOnce(In) -> FutOutF,
    FutOutG: Future<Output = Out>,
    G: FnOnce(Mid) -> FutOutG,
{
    type Output = Out;

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        // here comes the magic
    }
}

值转换如下: In -> Mid -> Out ,其中FG是我们的组合函数,它们的 output 相应地是FutOutFFutOutG 最后Future::poll实现:

let this = unsafe { self.get_unchecked_mut() };

let state = this.state.take();

match state {
    None => Poll::Pending, // invalid state
    Some(State::Initial(i, f, g)) => {
        let fut = f(i);
        this.state = Some(State::FirstAwait(fut, g));
        cx.waker().wake_by_ref();
        Poll::Pending
    }
    Some(State::FirstAwait(mut fut, g)) => {
        let val = match unsafe { Pin::new_unchecked(&mut fut) }.poll(cx) {
            Poll::Ready(v) => v,
            Poll::Pending => {
                this.state = Some(State::FirstAwait(fut, g));
                return Poll::Pending;
            }
        };
        let fut = g(val);
        this.state = Some(State::SecondAwait(fut));
        cx.waker().wake_by_ref();
        Poll::Pending
    }
    Some(State::SecondAwait(mut fut)) => {
        match unsafe { Pin::new_unchecked(&mut fut) }.poll(cx) {
            Poll::Ready(v) => Poll::Ready(v),
            Poll::Pending => {
                this.state = Some(State::SecondAwait(fut));
                Poll::Pending
            }
        }
    }
}

我避免使用任何库以使其“简单”,通常不安全的部分由pin-projectfutures::pin_mut state 管理比较复杂,建议重新检查实现,可能有错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM