Compiler unable to infer lifetime for async closure

I am trying to write a function that returns an async closure, but running into an issue where the compiler is unable to infer lifetimes. I am able to write an equivalent sync version (included in the playground code) just fine. It is only for async that I am running into issues.

Playground

use std::{future::Future, pin::Pin};

pub struct SystemContext {
    pub data: usize,
}

pub struct RequestContext<'a> {
    pub data: &'a str,
}

pub async fn async_compute<'r>(
    system_context: &'r SystemContext,
    request_context: &RequestContext<'r>,
    input: String,
) -> String {
    // real code makes an async call 
    format!("{} {}", input.repeat(system_context.data), request_context.data)
}

type AsyncComputeFn<'r> =
    Box<dyn Fn(&RequestContext<'r>, String) -> Pin<Box<dyn Future<Output = String> + 'r>> + 'r>;

pub fn async_curried_compute<'r>(system_context: &'r SystemContext) -> AsyncComputeFn<'r> {
    Box::new(move |request_context: &RequestContext<'r>, input: String| {
        Box::pin(async move { async_compute(system_context, request_context, input).await })
    })
}

// request_context in AsyncComputeFn doesn't specify a lifetime 
// so that I can write code such as the following. The computation 
// depends only on the content of request_context, in any case
async fn use_async_compute(async_compute_fn: AsyncComputeFn<'_>) -> String {
    let request_context = RequestContext {
        data: "request data",
    };
    let input = "input data".to_string();
    async_compute_fn(&request_context, input).await
}

#[tokio::main]
async fn main() {
    let system_context = SystemContext { data: 5 };

    println!(
        "async {}",
        use_async_compute(async_curried_compute(&system_context)).await
    );
}

With this, I get the following error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:42:29
   |
42 |         Box::pin(async move { async_compute(system_context, request_context, input).await })
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
  --> src/main.rs:41:14
   |
41 |       Box::new(move |request_context: &RequestContext<'r>, input: String| {
   |  ______________^
42 | |         Box::pin(async move { async_compute(system_context, request_context, input).await })
43 | |     })
   | |_____^
note: ...so that the types are compatible
  --> src/main.rs:42:29
   |
42 |         Box::pin(async move { async_compute(system_context, request_context, input).await })
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `(&SystemContext, &RequestContext<'_>, String)`
              found `(&'r SystemContext, &RequestContext<'r>, String)`
note: but, the lifetime must be valid for the lifetime `'r` as defined here...
  --> src/main.rs:40:30
   |
40 | pub fn async_curried_compute<'r>(system_context: &'r SystemContext) -> AsyncComputeFn<'r> {
   |                              ^^
note: ...so that the types are compatible
  --> src/main.rs:42:9
   |
42 |         Box::pin(async move { async_compute(system_context, request_context, input).await })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Pin<Box<(dyn Future<Output = String> + 'r)>>`
              found `Pin<Box<dyn Future<Output = String>>>`

For more information about this error, try `rustc --explain E0495`.

I am not sure what I need to change to make the compiler happy.

Here:

type AsyncComputeFn<'r> =
    Box<dyn Fn(&RequestContext<'r>, String) -> Pin<Box<dyn Future<Output = String> + 'r>> + 'r>;

pub fn async_curried_compute<'r>(system_context: &'r SystemContext) -> AsyncComputeFn<'r> {
    Box::new(move |request_context: &RequestContext<'r>, input: String| {
        // call this 'x ------------^ captured here --------v
        Box::pin(async move { async_compute(system_context, request_context, input).await })
    })
}

Your future is capturing the &'x RequestContext<''r> and thus can only live for 'x. Which implies use_async_compute is problematic as you're trying to return something holding on to a reference to a local (where as the sync version completes within the function body).

Not sure if it works for you, but maybe you need to be taking an owned RequestContext.

1 Like

That was it! Thank you so much @quinedot. Taking an owned RequestContext indeed works for me.