About using lifetime in closures

I have the following asynchronous execution functions:

async fn actor(a: &str) -> String{
    return a.to_string();
}

I need to wrap it with the following function:

fn wrap<'a>() -> Box<dyn Fn(&str) -> Box<dyn Future<Output=String> + Unpin + 'a> + 'a> {
    return Box::new(move |input| Box::new(Box::pin(actor(input))));
}

Then run it in the following code:

#[tokio::main]
async fn main() {
    let func = wrap();
    let output = func("test").await;
    println!("{:#?}", output);
}

But the compiler gives the following error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter '_ in function call due to conflicting requirements
  --> src/main.rs:15:52
   |
15 |     return Box::new(move |input| Box::new(Box::pin(actor(input))));
   |                                                    ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
  --> src/main.rs:15:21
   |
15 |     return Box::new(move |input| Box::new(Box::pin(actor(input))));
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:15:58
   |
15 |     return Box::new(move |input| Box::new(Box::pin(actor(input))));
   |                                                          ^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
  --> src/main.rs:14:9
   |
14 | fn wrap<'a>() -> Box<dyn Fn(&str) -> Box<dyn Future<Output=String> + Unpin + 'a> + 'a> {
   |         ^^
note: ...so that the types are compatible
  --> src/main.rs:15:34
   |
15 |     return Box::new(move |input| Box::new(Box::pin(actor(input))));
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Box<(dyn Future<Output = String> + Unpin + 'a)>`
              found `Box<dyn Future<Output = String> + Unpin>`

I don't know how to fix it, can anyone offer some help?

You need to use HRTB, not generic lifetime:

Box<dyn for<'a> Fn(&'a str) -> Box<dyn Future<Output = String> + Unpin + 'a>>

Because not your caller choose the lifetime, but you say "for any lifetime...".

3 Likes

It works, thank you very much!

Instead of using Box::new(Box::pin(...)) double-indirection to produce a Box<dyn Future<...> + Unpin via Box<Pin<Box<{future type}>>>, it might be a nicer approach to produce a Pin<Box<dyn Future<...>> instead, which requires one less level of indirection and is the "more typical" erased future type as far as I know. Depending on context, other marker traits such as Send might be needed, too. For example the type alias BoxFuture from the futures crate is often a reasonable default and it's a shorthand for Pin<Box<dyn Future<...> + Send + 'a>>.Theres a Send-less variant, too, for when you don't spawn the futures in a multi-threaded executor and want to lift the (then unnecessary) restrictions that come with the Send bound.

1 Like

That was an amazing answer, I spent some time looking into using it and now it's working, again thank you very much!