Return async closure generator

Greetings!

Does there is any way to return generator closure, which returns futures without boxing it?
This work fine:

    #[inline(always)]
    pub fn get_sender_fn(
        &self,
    ) -> impl Fn(u64) -> Pin<Box<dyn Future<Output = Result<(), SendError<u64>>>>> {
        let tx = self.tx.clone();
        move |id| {
            let tx = tx.clone();
            Box::pin(async move { tx.send(id).await })
        }
    }

but with generic I'm getting errors:

    #[inline(always)]
    pub fn get_sender_fn<F, Fut>(&self) -> F
    where
        F: Fn(u64) -> Fut,
        Fut: Future<Output = Result<(), SendError<u64>>>,
    {
        let tx = self.tx.clone();
        move |id| {
            let tx = tx.clone();
            async move { tx.send(id).await }
        }
    }
error[E0308]: mismatched types
  --> src/main.rs:42:9
   |
36 |       pub fn get_sender_fn<F, Fut>(&self) -> F
   |                            -                 - expected `F` because of return type
   |                            |
   |                            this type parameter
...
42 | /         move |id| {
43 | |             let tx = tx.clone();
44 | |             async move { tx.send(id).await }
45 | |         }
   | |_________^ expected type parameter `F`, found closure
   |
  ::: /Users/asinotov/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/future/mod.rs:72:43
   |
72 |   pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
   |                                             ------------------------------- the found opaque type
   |
   = note: expected type parameter `F`
                     found closure `[closure@src/main.rs:42:9: 45:10]`
   = help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F`

Generics don't work because if Fut is a generic, then the caller of the function is the one who chooses what Fut should be. If they pick Fut = JoinHandle<Result<(), SendError<u64>>, then you would be returning the wrong future type.

The only workaround I know of is to define a helper trait:

trait MyAsyncFn<T, U>: Fn(T) -> Self::Fut {
    type Fut: Future<Output = U>;
}

impl<T, U, F, Fut> MyAsyncFn<T, U> for F
where
    F: Fn(T) -> Fut,
    Fut: Future<Output = U>,
{
    type Fut = Fut;
}

Then you can use the return-type impl MyAsyncFn<u64, Result<(), SendError<u64>>.

Edit: You don't need a call method in the trait because of the : Fn(T) -> Self::Fut in the trait definition.

2 Likes