Wrapping trait that returns a future

Hi!

I'm using a library (lambda_runtime) that provides a Handler trait with a function returning a Future, and has a function run that takes something that implements that trait.

The implementation provided by the crates wraps an async function and calls it directly.

I'm trying to create a wrapper that will take a Handler implementation, and itself implement Handler, so that I can do something before/after the function is called. How can I await the Future in the sync trait so that I can do something after its execution, while providing a valid Self::Fut type for the trait?

This works, but I cannot do something after the function call, since I don't await it:

pub struct SampleHandler<H> {
    h: H,
}

impl<H> SampleHandler<H> {
    pub fn new<A, B>(h: H) -> Self
    where
        H: Handler<A, B>,
    {
        Self { h }
    }
}

impl<H, A, B, Fut> Handler<A, B> for SampleHandler<H>
where
    H: Handler<A, B, Fut = Fut>,
    Fut: Future<Output = Result<B, H::Error>>,
{
    type Error = H::Error;
    type Fut = Fut;

    fn call(&self, event: A, context: Context) -> Self::Fut {
        println!("Something before the function call");
        let res = self.h.call(event, context);
        println!("Something that runs as the function runs, but it should run after");
        res
    }
}

Things I've tried

Wrapping into an async block/function

I've tried to either wrap the logic into an async {} block, or use an async fn, but this doesn't work. They both fail with the following error message:

mismatched types
expected type Fut
found opaque type impl Future
type parameters must be constrained to match other types

    // in the trait implementation
    fn call(&self, event: A, context: Context) -> Self::Fut {
        async {
            println!("Something before the function call");
            let res = self.h.call(event, context).await;
            println!("Something that runs as the function runs, but it should run after");
            res
        }
    }
async fn wrapper_func<A, B, H>(h: H, event: A, context: Context) -> Result<B, H::Error>
where
    H: Handler<A, B>,
{
    println!("Starting invocation");
    let res = h.call(event, context).await;
    // Since `res` is a Future, this next line could run before the previous line finishes.
    println!("Finishing invocation");
    res
}

impl<H, A, B, Fut> Handler<A, B> for SampleHandler<H>
where
    H: Handler<A, B, Fut = Fut>,
    Fut: Future<Output = Result<B, H::Error>>,
{
    type Error = H::Error;
    type Fut = Fut;

    fn call(&self, event: A, context: Context) -> Self::Fut {
        wrapper_func(self.h, event, context)
    }
}

Specifying impl Future for the Fut type

I've also tried to do the following, but that doesn't work since impl Trait for type aliases are unstable.

impl<H, A, B, Fut> Handler<A, B> for SampleHandler<H>
where
    H: Handler<A, B, Fut = Fut>,
    Fut: Future<Output = Result<B, H::Error>>,
{
    type Error = H::Error;
    type Fut = impl Future<Output = Result<B, H::Error>>;

    fn call(&self, event: A, context: Context) -> Self::Fut {
        wrapper_func(self.h, event, context)
    }
}

You can't use the actual type yet. You have to box it. Box::pin(your_async_fn()).

1 Like

Thanks a lot! I managed to make it work by using Box::pin and with this sample implementation.

Here's the result:

impl<H, A, B, Fut> Handler<A, B> for SampleHandler<H>
where
    H: Handler<A, B, Fut=Fut>,
    Fut: Future<Output = Result<B, H::Error>> + 'static,
{
    type Error = H::Error;
    type Fut = Pin<Box<dyn Future<Output = Result<B, H::Error>> + 'static>>;

    fn call(&self, event: A, context: Context) -> Self::Fut {
        println!("Starting invocation");

        let fut = (self.h).call(event, context);
        let fut = async move {
            let res = fut.await;
            println!("Invocation finished");
            res
        };
        Box::pin(fut)
    }
}
1 Like