Can an async fn take an async fn as argument?

If so, what does the function declaration / signature look like ?

An async function is just a function returning a Future

async fn foo<F, R>(cb: F) -> R::Output
where
    F: Fn() -> R,
    R: std::future::Future,
{
    cb().await
}

Edit a few hours later : would it be possible to define Fn{Mut|Once}Async<Args, Output=Ret> traits like the Fn{Mut/Once} traits as shorthand FnAsync(Args) -> Ret ? (I imagine it would need the async traits feature and the associated type machinerie)

1 Like

It’s absolutely possible to define short-hands for those kinds of trait bounds. It’s just not possible to be generic over the number of function arguments (on stable rust), so you’ll have to incorporate that number into the trait names as well, and live with some upper limit.

E.g.

use paste::paste;
use std::future::Future;

macro_rules! define_async_fn_traits {
    ($($J:literal)+) => {
        define_async_fn_traits!{
            [Once][] $($J)+
        }
        define_async_fn_traits!{
            [Mut][] $($J)+
        }
        define_async_fn_traits!{
            [][] $($J)+
        }
    };
    ([$($FNTYPE:ident)?][$($I:literal)*] $N:literal $($J:literal)*) => {
        paste!{
            trait [<Fn $($FNTYPE)? Async $N>]<$([<Arg $I>]),*>
                : [<Fn $($FNTYPE)?>]($([<Arg $I>]),*) -> <Self as [<Fn $($FNTYPE)? Async $N>]<$([<Arg $I>]),*>>::OutputFuture
            {
                type OutputFuture: Future<Output = <Self as [<Fn $($FNTYPE)? Async $N>]<$([<Arg $I>]),*>>::Output>;
                type Output;
            }
            impl<F: ?Sized, Fut, $([<Arg $I>]),*> [<Fn $($FNTYPE)? Async $N>]<$([<Arg $I>]),*> for F
            where
                F: [<Fn $($FNTYPE)?>]($([<Arg $I>]),*) -> Fut,
                Fut: Future,
            {
                type OutputFuture = Fut;
                type Output = Fut::Output;
            }
        }
        define_async_fn_traits!{
            [$($FNTYPE)?][$($I)* $N] $($J)*
        }
    };
    ([$($FNTYPE:ident)?][$($I:literal)*]) => {};
}

define_async_fn_traits!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32);

////////////////////////////////////////////////////////////////////////////////
// demonstration:

async fn foo<F>(f: F) -> u32
where
    F: FnOnceAsync3<String, bool, f32, Output = u32>,
{
    f("hello".into(), true, 42.).await
}

async fn bar(_: String, _: bool, _: f32) -> u32 {
    42
}

async fn usage() {
    foo(bar).await;
}

(playground)

1 Like

Is there a shorter way to write this, something like:

async fn foo(cb: &'static async fn ...) 

? Or we are stuck with the two generics.

With the macros written by @steffahn it would be :

async fn foo<F>(f: F) -> <F as FnOnceAsync0>::Output
where
    F: FnOnceAsync0,
{
    f().await
}

You know what... I’ve put these traits in a crate now :smiley: (with slightly different name using AsyncFn… instead of FnAsync…).

https://docs.rs/async_fn_traits

3 Likes

Or something like

async fn foo<O>(f: impl FnOnceAsync0<Output = O>) -> O {
    f().await
}

would also work

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.