簡體   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