Skip to content

Misleading lifetime error with for loop and async capture #113121

@conradludgate

Description

@conradludgate

Code

struct Foo(String);
struct Bar;
fn foo(foos: &[Foo], bar: &'static Bar) {
    let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
    for foo in foos {
        let tx = tx.clone();
        tokio::spawn(async move {
            tx.send(foo.0.clone()).unwrap();
        });
    }
}

Current output

error[E0521]: borrowed data escapes outside of function
   --> src/lib.rs:5:16
    |
  3 | fn foo(foos: &[Foo], bar: &'static Bar) {
    |        ----  -       --- `bar` declared here, outside of the function body
    |        |     |
    |        |     let's call the lifetime of this reference `'1`
    |        `foos` is a reference that is only valid in the function body
  4 |     let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
  5 |     for foo in foos {
    |                ^^^^
    |                |
    |                `foos` escapes the function body here
    |                argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.

Desired output

I'm not sure what the perfect diagnostic would be here.

If you were the modify the code and not use a for loop/slice, you get something closer to this:

error[E0521]: borrowed data escapes outside of function
   --> src/lib.rs:517:9
    |
  3 |   fn foo(foos: &[Foo], bar: &'static Bar) {
    |          ---   -       --- `bar` declared here, outside of the function body
    |          |     |
    |          |     let's call the lifetime of this reference `'1`
    |          `foos` is a reference that is only valid in the function body
...
  7 | /         tokio::spawn(async move {
  8 | |             tx.send(foo.0.clone()).unwrap();
  9 | |         });
    | |          ^
    | |          |
    | |__________`foos` escapes the function body here
    |            argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.

If you modify the original code to use std::sync::mpsc/std::thread::spawn you get the same error above.

But the

--- `bar` declared here, outside of the function body

is still a red herring. I think the perfect diagnostic would point to foo.0.clone and the tokio::spawn's F: 'static requirement.

Rationale and extra context

The problem is that the foo is moved by reference, so the task is not 'static. Pointing the error at the for loop is incorrect as it's not the culprit of the issue. This might be a lifetime inference bug

Other cases

No response

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsT-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