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!

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.