Repository: rust
Issue: confusing "lifetime mismatch" error when spawning task
Labels: A-diagnostics,T-compiler,C-bug,A-async-await,AsyncAwait-Triaged,E-needs-mcve,requires-nightly,D-confusing

_suggested labels: A-diagnostics, A-async-await, D-confusing, requires-nightly_

## Introduction

Hey diagnostics team. I'm currently helping a pal debug some code, and we're experiencing a particularly gnarly error. We haven't been able to figure out what's going wrong, so we can't provide a repro either. So I'm opening this issue so folks can come along on the journey we're on, and maybe we can figure out both what the root error is -- and how to improve the error messages -- together!

## Setting the scene

The current project lives on the `spawn` branch of the `peermap-ingest` project. To get started do:

```sh
$ git clone https://github.com/substack/peermaps-ingest
$ git checkout origin spawn   # latest commit at time of writing is: ebb442c8716a0c6fa5d511756fcde940726f6be4
$ rustup override set nightly # rust version used at the time of writing is 1.54.0-nightly MSVC
$ cargo check
```

The following code is where our error points us to. We're pushing `async-std` `task::JoinHandle` into a `Vec` so we can await all handles together later on. Each task processes a command, which interacts with a backing store asynchronously in the background. If an error occurs, we log it. The `async move {}` block's generated future returns a `()`.

```rust
for _ in 0..nthreads {
    let this = self.clone();
    let r = receiver.clone();
    work.push(async_std::task::spawn(async move {
        loop {
            if r.is_closed() {
                break;
            }
            if let Ok((key, value)) = r.recv().await {
                let res: Result<(), Error> = match decode(&key.data, &value) {
                    Err(e) => Err(e.into()),
                    Ok(Decoded::Node(node)) => this.create_node(&node).await,
                    Ok(Decoded::Way(way)) => this.create_way(&way).await,
                    Ok(Decoded::Relation(relation)) => this.create_relation(&relation).await,
                };
                if let Some(f) = this.reporter.lock().await.as_mut() {
                    match res {
                        Err(e) => f(Phase::Process(), Err(e.into())),
                        Ok(r) => f(Phase::Process(), Ok(r)),
                    }
                }
            }
        }
    }));
}

```

Running the code generates the following output:

```
PS C:\Users\yoshu\Code\peermaps-ingest> cargo c
    Checking peermaps-ingest v1.0.2 (C:\Users\yoshu\Code\peermaps-ingest)
error[E0308]: mismatched types
   --> src\lib.rs:148:17
    |
148 |       work.push(async_std::task::spawn(async move {
    |                 ^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
    |
    = note: expected type `Result<_, Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>`
               found enum `Result<_, Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>`
note: the lifetime requirement is introduced here
   --> C:\Users\yoshu\scoop\persist\rustup\.cargo\registry\src\github.com-1ecc6299db9ec823\async-std-1.9.0\src\task\spawn.rs:28:29
    |
28  |     F: Future<Output = T> + Send + 'static,
    |                             ^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `peermaps-ingest`
```

## What are we looking at?

The main error describes a: "lifetime mismatch". It then points to a call site, highlights the type signatures, and finally points to where the requirement was defined. However this has some issues:

- the types in `note: expected type` do not carry any explicit lifetimes, and look otherwise identical to each other
- `note: the lifetime requirement is introduced here` points to the `Send` param in `async_std::task::spawn`. That's not a lifetime, which makes it confusing.
- we have to assume that the mention of "lifetime" here refers to the `'static` lifetime in `async_std::task::spawn`, but we cannot know for sure.
- in fact, given `Send` is highlighted in the second output makes it unclear whether the issue _actually_ is that we are trying to pass a `!Send` item to a function which required the item is `Send`.
- the mention of `Result<_, Box<dyn Error + ...>>` is odd, since the return type of the `Future` is `()`. What error is being referred to here? It's unclear!

## What have we tried so far?
- I've added `+ 'static` notations to every `Error` definition in the code (and dependencies) to no change
- I've replaced the anonymous `async move {}` block with an `async fn` to no change
- I've manually gone through the function calls and into dependencies, commenting out code to find the root cause -- but I couldn't find any return types which were `!Send` or non-`'static` (after having done the error type fix as well)

## What could be done to help solve this?

- It's unclear whether it's an issue with `Send` or an issue with a lifetime (if so: which lifetime?). Clearing that up would be a first step.
- The two types in `hint: expected type` should not be identical. If a lifetime is different, it should probably show up.
- The link between `async_std::task::spawn` in the source, and the error in the `hint: expected type` is unclear. It'd be nice to know _which_ `Error` type is being referred to -- even if it's only shown with `--verbose` output. Needing to manually step through code takes a lot of time, where supposedly the compiler already knows this information (even if not displayed in the output).

## Conclusion

I hope this is enough information for someone to take a look at this. I think we're hitting a particularly gnarly combination of diagnostics failures here, which when put together make it hard to debug. Thanks heaps!

cc/ @substack