Async: Constrain lifetime of Fut type param to lifetime of for<'func>F: Fn(..) -> Fut

Heya, I'm trying to implement a generic async function, which delegates to a passed in subfunction that returns a future. The minimal generic function looks like this (playground):

async fn generic_fn<'f, T, F, Fut>(n: &'f u16, f: F) -> Result<(), AppErr>
    for<'func> F: FnOnce(&'func Ctx<'f>) -> Fut,
    Fut: Future<Output = Result<T, AppErr>>,
    T: std::fmt::Display,
    let ctx = Ctx(n);
    let t = f(&ctx).await?;


so that it can be used like this:

generic_fn(&1, specific_fun_1).await?;
generic_fn(&2, specific_fun_2).await?;

async fn specific_fun_1(ctx: &Ctx<'_>) -> Result<u32, AppErr> {

async fn specific_fun_2(ctx: &Ctx<'_>) -> Result<u64, AppErr> {
Error in the minimal example
error[E0308]: mismatched types
  --> src/
14 |     generic_fn(&1, specific_fun_1).await?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   = note: expected opaque type `impl for<'func> Future<Output = Result<u32, Box<(dyn std::error::Error + 'static)>>>`
              found opaque type `impl Future<Output = Result<u32, Box<(dyn std::error::Error + 'static)>>>`
   = help: consider `await`ing on both `Future`s
   = note: distinct uses of `impl Trait` result in different opaque types
note: the lifetime requirement is introduced here
  --> src/
24 |     for<'func> F: FnOnce(&'func Ctx<'f>) -> Fut,
   |                                             ^^^

Is there a way to tell Rust that Fut should be constrained by the 'func lifetime?

In the real code I actually get something less helpful, but I think solving the minimal example would help:

error: higher-ranked lifetime error
  --> examples/app_cycle/src/cmds/
22 |         EnvCmd::run(output, StatesSavedReadCmd::exec).await
   |         ^^^^^^^^^^^

Box::pin is the easiest way to do it. Otherwise, it won't be ergonomic. Also see How to express that the Future returned by a closure lives only as long as its argument?

BTW, I've added the code here as an example of showing how it can be acheived with my crate async_closure:

1 Like

Thank you very much for the links! That's some advanced code :smile:

I've gone with the Box::pin route via FutureExt::boxed()