Skip to content

Closure in async block fails to compile when it should, issues incorrect/misleading diagnostic #96128

@cdhowie

Description

@cdhowie

I've put the code reproducing this issue in the cdhowie/warp-lifetime-issue-repro repository as it has a dependency on warp. I have not been able to build a reproduction of this issue outside of warp or rocket-rs, but I believe the issue is with the compiler nonetheless, for a few reasons. I have been able to reproduce the issue on all of the following Rust Docker images:

  • docker.io/library/rust:1.59-bullseye (d6c4db7b2530)
  • docker.io/library/rust:1.60-bullseye (5593c6ce4c4e)
  • docker.io/rustlang/rust:nightly-bullseye (cf477c958fa3 -- 1.62.0-nightly (e7575f9 2022-04-14))

The summary is that passing a very simple non-capturing closure in an async route handler causes a lifetime mismatch error, referencing a type that is not used or demanded in the handler:

error[E0308]: mismatched types
   --> src/main.rs:39:6
    |
39  |     .and_then(|_id, provider: Arc<T>| async move {
    |      ^^^^^^^^ lifetime mismatch
    |
    = note: expected type `for<'r> FnOnce<(&&Item,)>`
               found type `for<'r> FnOnce<(&'r &Item,)>`
note: this closure does not fulfill the lifetime requirements
   --> src/main.rs:42:41
    |
42  |         let items = items.iter().filter(|item| !item.is_deleted());
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^
note: the lifetime requirement is introduced here
   --> /appsrc/.cache/registry/src/github.com-1ecc6299db9ec823/warp-0.3.2/src/filter/mod.rs:259:32
    |
259 |         F::Output: TryFuture + Send,
    |                                ^^^^

Nowhere in the handler is an FnOnce built, nor is one asked for by Iterator::filter() so at the very least the diagnostic is misleading. However, rewriting the code to use a free function instead of a closure allows the code to compile:

fn item_is_not_deleted(item: &&Item) -> bool {
    !item.is_deleted()
}

// Replace line 42 with:

let items = items.iter().filter(item_is_not_deleted);

I can't see any reason why a closure should fail here but a free function should work; I'd expect them to be effectively identical since the closure doesn't capture anything from its environment.

For more specific details, see the repository linked above. (I'm happy to copy the repro code directly into this issue if desired, just let me know.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitA-diagnosticsArea: Messages for errors, warnings, and lintsAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions