'Lifetime of a reference passed to async callback

I have async function to which I am passing async callback. The callback takes a reference as a parameter.

use core::future::Future;

async fn foo(bar: &u32) {}

async fn baz<F, Fut>(f: F)
where
    F: FnOnce(&u32) -> Fut,
    Fut: Future<Output = ()>,
{
    let test: u32 = 42;
    f(&test).await;
}

#[tokio::main]
async fn main() {
    baz(foo).await;
}

I am getting the following error if I try to build this (playground):

error[E0308]: mismatched types
  --> src/main.rs:16:5
   |
16 |     baz(foo).await;
   |     ^^^ lifetime mismatch
   |
   = note: expected associated type `<for<'_> fn(&u32) -> impl Future<Output = ()> {foo} as FnOnce<(&u32,)>>::Output`
              found associated type `<for<'_> fn(&u32) -> impl Future<Output = ()> {foo} as FnOnce<(&u32,)>>::Output`
   = note: the required lifetime does not necessarily outlive the empty lifetime
note: the lifetime requirement is introduced here
  --> src/main.rs:7:24
   |
7  |     F: FnOnce(&u32) -> Fut,
   |                        ^^^

I understand that it's not happy about the lifetime of the reference. However, I don't understand why.

  • We borrow "test"
  • We execute callback f (which is "foo")
  • There is no way for baz exits before f is done

So, it looks like there is no way for the borrow to outlive the place where test is declared.

What am I missing?



Solution 1:[1]

The future returned by foo() has a hidden lifetime. The desugared signature is like:

fn foo<'a>(bar: &'a u32) -> impl Future<Output = ()> + 'a {
    async move {}
}

This is done so the function can hold bar across .await points. Sadly, what it means is that the function does not fulfill baz()'s bounds. The errors are obfuscated because the lifetime is hidden, but this is what the compiler is trying to tell you: the bound should be something like where F: for<'a> FnOnce(&'a u32) -> impl Future<Output = ()> + 'a, but you cannot express that in current Rust.

For more and potential solutions, see for example:

Solution 2:[2]

Just for solving, you can use Box instead of references.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Chayim Friedman
Solution 2 user-id-14900042