Rust: async closures have concrete argument lifetimes

Created on 21 Mar 2019  路  6Comments  路  Source: rust-lang/rust

#![feature(async_await, futures_api)]

use std::future::Future;

trait Foo<'a> {
    type Future: Future<Output = u8> + 'a;

    fn start(self, f: &'a u8) -> Self::Future;
}

impl<'a, Fn, Fut> Foo<'a> for Fn
where
    Fn: FnOnce(&'a u8) -> Fut,
    Fut: Future<Output = u8> + 'a,
{
    type Future = Fut;

    fn start(self, f: &'a u8) -> Self::Future { (self)(f) }
}

fn foo<F>(f: F) where F: for<'a> Foo<'a> {
    let bar = 5;
    f.start(&bar);
}

fn main() {
    foo(async move | f: &u8 | { *f });

    foo({ async fn baz(f: &u8) -> u8 { *f } baz });
}

(playground) currently errors with

error: implementation of `Foo` is not general enough
  --> src/main.rs:27:5
   |
27 |     foo(async move | f: &u8 | { *f });
   |     ^^^
   |
   = note: Due to a where-clause on `foo`,
   = note: `Foo<'1>` would have to be implemented for the type `[closure@src/main.rs:27:9: 27:37]`, for any lifetime `'1`
   = note: but `Foo<'_>` is actually implemented for the type `[closure@src/main.rs:27:9: 27:37]`, for some specific lifetime `'2`

You can see that the async fn correctly satisfies the HRLB required by foo, but the async closure has a concrete lifetime for the argument instead of being for any lifetime.

A-async-await AsyncAwait-Triaged C-bug F-async_closures T-compiler requires-nightly

Most helpful comment

I think this is actually a problem with regular closures. It's just a lot more noticeable with async closures. In the working example, replace:

foo({ async fn baz(f: &u8) -> u8 { *f } baz });

with:

foo({ async fn baz(f: &u8) -> u8 { *f } |f| baz(f) });

This isn't an async closure. It should have the same function signature as baz, taking a borrowed u8 and returning a future. However, it doesn't compile:

error[E0308]: mismatched types
  --> src/main.rs:15:5
   |
15 |     foo({ async fn baz(f: &u8) -> u8 { *f } |f| baz(f) });
   |     ^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(&'a u8,)>`
              found type `std::ops::FnOnce<(&u8,)>`

All 6 comments

@rustbot modify labels: A-async-await and T-compiler.

Interestingly, using trait aliases instead of the extra trait above gives a different error message, not sure exactly what it believes is wrong here (could easily be a trait alias issue rather than an async closure issue) (playground)

#![feature(async_await, futures_api, trait_alias, unboxed_closures)]

use std::future::Future;

trait Foo<'a> = FnOnce<(&'a u8,)> where <Self as FnOnce<(&'a u8,)>>::Output: Future<Output = u8> + 'a;

fn foo<F>(f: F) where F: for<'a> Foo<'a> {
    let bar = 5;
    f(&bar);
}

fn main() {
    foo(async move | f: &u8 | { *f });

    foo({ async fn baz(f: &u8) -> u8 { *f } baz });
}
error[E0477]: the type `impl std::future::Future` does not fulfill the required lifetime
  --> src/main.rs:13:5
   |
13 |     foo(async move | f: &u8 | { *f });
   |     ^^^
   |
   = note: type must outlive any other region

Marking as deferred since async closures are not considered part of the "core functionality" we are trying to stabilize.

I think this is actually a problem with regular closures. It's just a lot more noticeable with async closures. In the working example, replace:

foo({ async fn baz(f: &u8) -> u8 { *f } baz });

with:

foo({ async fn baz(f: &u8) -> u8 { *f } |f| baz(f) });

This isn't an async closure. It should have the same function signature as baz, taking a borrowed u8 and returning a future. However, it doesn't compile:

error[E0308]: mismatched types
  --> src/main.rs:15:5
   |
15 |     foo({ async fn baz(f: &u8) -> u8 { *f } |f| baz(f) });
   |     ^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(&'a u8,)>`
              found type `std::ops::FnOnce<(&u8,)>`

I just ran into this too: in relation to async/await, but in a situation that does not require a nightly compiler:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c0e8210afc5a1c7271064b7607c584b6

I think the "requires-nightly" label should be removed as this is clearly a broader problem that affects all closures.

It seems like it might be a problem with the type inference of the closures return type? It ends up inferring the return type to be impl Future instead of impl Future + 'a.

Was this page helpful?
0 / 5 - 0 ratings