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 typeFut
found opaque typeimpl 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)
}
}