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/on_get_index.rs
pub async fn on_request(req: &json::JsonValue) -> json::JsonValue
{
....
}

// in request/mod.rs
struct RequestHandler {
name: &'static str,
func: WHAT_TYPE_IS_IT?,
}
// 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

1 Like

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 ...
```
5 Likes

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/on_get_index.rs
pub
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
    $pub:vis
    async
    fn $fname:ident<$lt:lifetime> ( $($args:tt)* ) $(-> $Ret:ty)?
    {
        $($body:tt)*
    }
) => (
    $( #[$attr] )*
    #[allow(unused_parens)]
    $pub
    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/on_get_index.rs
#[macro_rules_attribute(dyn_async!)]
pub
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!
4 Likes

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 https://github.com/jeremyandrews/goose/pull/8/files (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 router.to_async(), simple async/.await examples by alsuren · Pull Request #281 · gotham-rs/gotham · GitHub . 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.