Type signature for async functions

Hello, everyone!

I cannot figure how to write a trait implementation for static async function when lifetimes in signature are involved.

use std::{convert::Infallible, future::Future};

trait Trait {}

impl<Fut> Trait for (for<'a, 'b> fn(&'a u8, &'b u16) -> Fut)
where
    Fut: Future<Output = Result<(), Infallible>>,
{
}

async fn lol(_: &u8, _: &u16) -> Result<(), Infallible> {Ok(())}

fn check<T: Trait>(_: T) {}

pub fn check2() {
    check(lol)
}

This doesn't complile at the moment with an error:

error[E0277]: the trait bound `for<'_, '_> fn(&u8, &u16) -> impl std::future::Future {lol}: Trait` is not satisfied
  --> src/lib.rs:16:11
   |
13 | fn check<T: Trait>(_: T) {}
   |    -----    ----- required by this bound in `check`
...
16 |     check(lol)
   |           ^^^ the trait `Trait` is not implemented for `for<'_, '_> fn(&u8, &u16) -> impl std::future::Future {lol}`

It seems that problem is somewhere with lifetime parameters in fns signature.

Try on Playground

The things are going relatively OK when it's tried for Fn traits. But I'd prefer to have one for static fns in my case as it will allow to provide trait implementation for other signatures and have custom trait implementations at once.

for<'a, 'b> fn(&'a u8, &'b u16) -> Fut is not the same type as for<'a, 'b> fn(&'a u8, &'b u16) -> Fut {lol}. The former is a function pointer, while the second refers to the specific function lol. You can try to use check(lol as fn(_, _) -> _).

Meh... I haven't noticed that each static function with identical signature represents a different type :confused:
So, the thing I wanted is impossible in current Rust's type system.

I cannot use as fn as the signature itself is quite insane.

So, the only option is to go with Fn trait and have wrapper types. The last thing that bothers me is using #![feature(unboxed_closures)]. However, compiler doesn't allow to get rid of it.

There is a far-from-ideal solution, which however works for me:

Well it seems that problem is deeper.

I've ended up with the situation that I cannot pass static async fn anywhere at all:

The only thing that works is to expose argument lifetimes as lifetime parameters. However that's not practical at all as doesn't allow me to call the actual function in a meaningful context. For such wrapping being useful there should be HRTB used (which is implicitly true if arguments are not restricted with any explicit lifetime parameters). However, using HRTB just doesn't allow to accept async fn pointers and I cannot figure out how to make it work.

Wow, how obscure could a half page of code be? Having been programming for decades the only thing I recognize there is "fn", "u8", "u16" and "Result". Cannot begin to fathom what the rest of it is even hinting at.

Is this how regular Rust code will look in the async world?

Have to wrap in a boxing function, can't use generic Fut as return is lifetime limited and impl Trait does not have capability.

No, the only code really relevant is the body with Ok(())

Nope. This is all more about library under-the-hood stuff to make regular async code a pleasure.

Thank you for the tip!

However, as I see, now "the problem" is shifted slightly to another place. The point is to accept arbitrary async fn with a given signature. So, I still have to have something generic, which accepts arbitrary async fn and converts it into one returning Boxed Future. Any ideas?

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