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>
where
    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?;
    println!("{t}");

    Ok(())
}

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> {
    Ok(u32::from(*ctx.0))
}

async fn specific_fun_2(ctx: &Ctx<'_>) -> Result<u64, AppErr> {
    Ok(u64::from(*ctx.0))
}
Error in the minimal example
error[E0308]: mismatched types
  --> src/main.rs:14:5
   |
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/main.rs:24:45
   |
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/env_status_cmd.rs:22:9
   |
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()

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.