Reputation: 515
async fn
returns an anonymous type that implements Future
, so if we want to use it as a callback, we need to convert the return value to a trait object.
I tried to write an function to do this, but I had some lifetime problems.
async fn
will return lifetime of all parameters, so the signature of callback also needs to. How can I add the lifetime to the return value of the callback?
use futures::future::{Future, FutureExt, LocalBoxFuture};
type Context = ();
type AsyncCb = Box<dyn for<'r> FnOnce(&'r Context) -> LocalBoxFuture<'r, ()>>;
fn normalize_async_cb<Fut: Future<Output = ()>>(f: for<'r> fn(&'r Context) -> Fut) -> AsyncCb
// how to add 'r for Fut? ^^^
{
let cb = move |ctx: &Context| f(ctx).boxed_local();
Box::new(cb)
}
Upvotes: 14
Views: 4057
Reputation: 3875
Rust does not support higher-kinded polymorphism, so you need to add a lifetime parameter to the AsyncCb
type:
use futures::future::{Future, FutureExt, LocalBoxFuture};
type Context = ();
type AsyncCb<'r> = Box<dyn FnOnce(&'r Context) -> LocalBoxFuture<'r, ()> + 'r>;
fn normalize_async_cb<'r, Fut: Future<Output = ()> + 'r>(f: fn(&'r Context) -> Fut) -> AsyncCb {
let cb = move |ctx: &'r Context| f(ctx).boxed_local();
Box::new(cb)
}
Aditionally, you can avoid a Box
by returning impl
trait:
fn normalize_async_cb<'r, Fut: Future<Output = ()> + 'r>(
f: fn(&'r Context) -> Fut,
) -> impl FnOnce(&'r Context) -> LocalBoxFuture<'r, ()> {
let cb = move |ctx: &'r Context| f(ctx).boxed_local();
cb
}
(The caller can then use Box::new(normalize_async_cb(…))
as type AsyncCb
if desired.)
Upvotes: 3