Function that takes a closure with mutable reference that returns a future

Hi, I have hit a type parameterized lifetime problem. In the following snippet:

  • foo is the API method that takes a seed value, and a closure.
  • The closure has 2 parameters, the seed value, and a borrowed value
  • The closure returns a future whose output is the seed's type

The code (playground):

use std::future::Future;

pub async fn bar() {
    let mut a = 16u16;
    foo(&mut a, 0u8, |count: u8, x: &mut u16| async move {
        baz(x);

        count + 1
    }).await;
}

pub async fn baz(x: &mut u16) -> bool {
    if *x == 1 {
        false
    } else {
        true
    }
}

pub async fn foo<F, Fut, R>(a: &mut u16, mut seed_return: R, f: F) -> R
where
    F: Fn(R, &mut u16) -> Fut,
    Fut: Future<Output = R>,
{
    for _ in 0u32..2u32 {
        seed_return = f(seed_return, a).await;
    }
    seed_return
}

with the error message:

error: lifetime may not live long enough
 --> src/lib.rs:5:47
  |
5 |       foo(&mut a, 0u8, |count: u8, x: &mut u16| async move {
  |  _____________________________________-_______-_^
  | |                                     |       |
  | |                                     |       return type of closure `impl Future` contains a lifetime `'2`
  | |                                     let's call the lifetime of this reference `'1`
6 | |         baz(x);
7 | |
8 | |         count + 1
9 | |     }).await;
  | |_____^ returning this value requires that `'1` must outlive `'2`

My issue is similar to the case as presented in rust-lang/rust#74497, which I adapted for the minimal example.

From Niko's comment, I understand that the reference being passed into the closure is valid for the lifetime of the closure, but not of the future that the closure produces.

My question is how can one tell Rust that the reference passed to the closure is valid for as long as the future, but not as long as foo?

You cannot do this without boxing the future. If you are ok with boxing the future, it looks like this:

pub async fn foo<F, R>(a: &mut u16, mut seed_return: R, f: F) -> R
where
    F: for<'a> Fn(R, &'a mut u16) -> Pin<Box<dyn Future<Output = R> + Send + 'a>>,
{
2 Likes

Thank you very much :bowing_man:!

How does one come to learn that that's the solution?

It's not something I would've discovered / thought of -- maybe due to lack of understanding. I've read the Pinning chapter in the async-book, but perhaps didn't grasp something.

Typically you ask on this forum.