Async closure (lifetime may not live long enough)

Async closure was just stabilized in 1.85, so I wanted to give it a try:

fn foo<F>(_: F) 
where
    F: AsyncFn(&()) -> &(),
{
}

fn main() {
    // ok
    foo(async |u: &()| { u });
    
    // error: lifetime may not live long enough
    let c = async |u: &()| { u }; 
    foo(c);
}

playground

error: lifetime may not live long enough
  --> src/main.rs:12:28
   |
12 |     let c = async |u: &()| { u }; 
   |                       -  - ^^^^^ returning this value requires that `'1` must outlive `'2`
   |                       |  |
   |                       |  return type of async closure `{async closure body@src/main.rs:12:28: 12:33}` contains a lifetime `'2`
   |                       let's call the lifetime of this reference `'1`

error[E0308]: mismatched types
  --> src/main.rs:13:5
   |
13 |     foo(c);
   |     ^^^^^^ one type is more general than the other
   |
   = note: expected reference `&()`
              found reference `&()`
note: the lifetime requirement is introduced here
  --> src/main.rs:3:24
   |
3  |     F: AsyncFn(&()) -> &(),
   |                        ^^^

It seems to me that the type inference fails when lifting the async closure into a variable. Is it related to this issue? Thanks!

this is not new, it's the same problem with closure type inference limitation, same for non-async situations as well:

fn foo<F>(_: F) where F: Fn(&()) -> &(),
{
}

fn main() {
    // ok
    foo(|u: &()| { u });
    
    // error: lifetime may not live long enough
    let c = |u: &()| { u }; 
    foo(c);
}

if you pass the closure directly to the function, because function arguments are coersion sites, it helps the type inference. but when you let-bind the closure to a variable, it is inferred with one concrete lifetime, instead of higher-order for-all lifetimes.

if you search keywords like "closure", "lifetime", "borrow", you'll find many previous threads discussing similar issues, such as this one

Thanks for the quick reply! Indeed this is the problem and if I wrap the variable value with hr_bump that coerces it to be HRTB and hence works.

Another workaround I found is using the nightly impl_trait_in_bindings feature to explicitly 'type' the async closure as HRTB:

#![feature(impl_trait_in_bindings)]

fn foo<F>(_: F) 
where
    F: AsyncFn(&()) -> &(),
{
}

fn main() {
    let c: impl for<'a> AsyncFn(&'a ()) -> &'a () = async |u: &()| { u };
    foo(c);
}

playground

Thanks again!

1 Like