Reputation: 81
I'd like to create a type alias to promote closures to functions without the user having to think about the parameter and return signatures of their functions. This would translate this bit of code:
async fn some_func(s: &mut Context<Props>) -> VNode {
// some function body...
}
into this bit of code:
static SomeFunc: FC<Props> = |ctx| async {
// some function body
};
In JavaScript, this would use the const closure function definition over the regular function shorthand.
For regular functions, this works fine:
type FC<T: Props> = fn(&mut Context<T>) -> Vnode;
Then, static closures are promoted to function pointers.
However, impl Future
cannot be used in a type alias (not even on 1.51 nightly), and I can't have generic trait bounds in type aliases either. I can't tell if this is possible, but am curious if there are ways for a type alias to work for async fns.
The API I'm designing takes functions as inputs (not structs or trait objects) and I'd like to make it easy to work with.
Upvotes: 6
Views: 919
Reputation: 4249
You cannot do this directly, because every async fn has a different return type, even if the future evaluates to the same type. This is because each async fn has its own unique Future
type that it returns.
To do this, you must use trait objects (dyn Fn
) instead of function pointers (fn()
) as the type. The type you are looking for is
use futures::future::BoxFuture;
type FC<T> = Box<dyn Send + Sync + for<'a> Fn(&'a mut Context<T>) -> BoxFuture<'a, Vnode>>;
The above type will act as a function that returns a Future
, but a conversion step is required to go from an ordinary async fn into a function of the above type.
Performing this conversion can be done as follows:
Box::new(|context| Box::pin(my_async_fn(context)))
It can also be written using the futures crate. The version that uses .boxed()
will in some cases help the type checker out and avoid some errors that the Box::pin
version can give.
use futures::future::FutureExt;
Box::new(|context| my_async_fn(context).boxed())
Since the specific type alias we are using in this case has lifetimes involved, there is no way to define a convenience conversion for the above. That said, when the arguments are not references, you can do this:
use futures::future::BoxFuture;
type AsyncFnPtr = Box<dyn Send + Sync + Fn(SomeArg) -> BoxFuture<'static, RetValue>>;
fn convert<F, Fut>(func: F) -> AsyncFnPtr
where
F: Send + Sync + 'static,
F: Fn(SomeArg) -> Fut,
Fut: Send + 'static,
Fut: Future<Output = RetValue>,
{
Box::new(|context| Box::pin(func(context)))
}
Additional resources:
for<'a>
mean?+ 'static
not result in a memory leak?Upvotes: 5