How to store Async Function Pointer?

Hello everybody,
I've been trying to save something like the entry of request handlers, so that i can query and call them later.
to be simple, I got code like this:

// in request/
pub async fn on_request(req: &json::JsonValue) -> json::JsonValue

// in request/
struct RequestHandler {
name: &'static str,
// and then i want use it like this:
// let req = some json::JsonValue
let handler = get_handler("on_get_index");
handler.func(&req) // returning resp: json::JsonValue

so what type is the func field, to store functions like on_request?
I've been trying for hours, and I found that the on_request token is just like a type which MakeSomeFuture, and I cannot find out what I was asking.

P.S. I want to use the RequestHandler with inventory, as in my previous post


async fn (...) -> Ret has a signature of fn(...) -> impl Future<Output = Ret>.

Given your inventory use case, generics are out of the question, so I'd say go for the following type:

fn (&'_ json::JsonValue) ->
    Pin<Box<dyn // owned trait object
        Future<Output = json::JsonValue> // future API / pollable
        + Send // required by non-single-threaded executors
        + '_ // may capture `req`, which is only valid for the `'_` lifetime

and to set func to some given function (e.g., on_request), you will need to wrap it as follows:

let func = |req| Box::pin(on_request(req));

Aside: you can color Rust code snippets by writing triple quotes around it:

pub async fn ...

If you do want to store the function producing the future, then it should be possible to have the type as Box<dyn Fn(Request) -> Box<dyn Future<Output = Ret>>. The key is that each async fn has a unique return type, so two async fns never even have the same function signature.

To fix this, you'll need to wrap your async fns in another function - maybe a closure, like |req| Box::new(func(req)). That way all the functions you store get a unified return type - Box<dyn Future<...>>, so you can store them all in the same structure.

Box<dyn Future<...>> is sadly not a future, in the sense that it cannot be polled given the Pin<&mut Self> receiver on Future::poll().

So you either add an Unpin bound (not recommended, it will make many async fns stop working), or you wrap the Box in a Pin< _ >, by using Box::pin() instead of Box::new().

By the way, if you are using #[async_trait] you shouldn't even be needing that wrapping since the proc macro does it automagically for you.

Also, you can directly define your async fns as:

type DynFut<'lt, T> = ::std::pin::Pin<Box<dyn 'lt + Send + ::std::future::Future<Output = T>>>;

// in request/
fn on_request (req: &'_ json::JsonValue) -> DynFut<'_, json::JsonValue>
    Box::pin(async move {
        // body of the function

For simple function signatures, you can define that transformation with a macro_rules! macro:

macro_rules! dyn_async {(
    $( #[$attr:meta] )* // includes doc strings
    fn $fname:ident<$lt:lifetime> ( $($args:tt)* ) $(-> $Ret:ty)?
) => (
    $( #[$attr] )*
    fn $fname<$lt> ( $($args)* ) -> ::std::pin::Pin<::std::boxed::Box<
        dyn ::std::future::Future<Output = ($($Ret)?)>
            + ::std::marker::Send + $lt
        ::std::boxed::Box::pin(async move { $($body)* })

which, thanks to #[macro_rules_attribute], would let you do:

// in request/
async fn on_request<'req> (req: &'req json::JsonValue) -> json::JsonValue
    // body of the function

// ...

let func = on_request; // no need for the closure that `Box::pin`s!

Thank you so much for posting this. I was banging my head against this for ages, and getting sent in circles by the borrow-checker. In the end I stole your macro and type definition, and used it in (very much work-in progress at the moment).

I have come across this "how do you specify the lifetime of an async function that borrows its arguments" problem before, when trying to write patches to gotham . In gotham's case, they have a handler function type that takes ownership of a context object (State) and returns Box<dyn Future<Output = Result<(State, Response), (State, Err)>>. I managed to make a helper function that accepts an async function and boxes the result for you, but I never managed to go all the way and make a helper that allows you to borrow State.

Is there a way of specifying lifetimes that allows you to pass in a plain old async fn which borrows its arguments?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.