rust-lang / rust-lang/rust

Output type mismatch misleadingly talks about mismatched async closure types

Open
#161,619 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
use futures::{Stream, StreamExt, TryStreamExt};

async fn async_example(iter: impl Stream) {
    iter.then(async |x| Ok(x))
        .try_for_each_concurrent(8, async |_| { Ok(String::new()) })
}
Current output
error[E0308]: mismatched types
 --> src/lib.rs:5:47
  |
5 |         .try_for_each_concurrent(8, async |_| { Ok(String::new()) })
  |                                               ^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `String`
  |
  = note: expected `async` closure body `{async closure body@src/lib.rs:5:47: 5:68}` (`()`)
             found `async` closure body `{async closure body@src/lib.rs:5:47: 5:68}` (`String`)
  = note: no two async blocks, even if identical, have the same type
  = help: consider pinning your async block and casting it to a trait object

error[E0308]: mismatched types
 --> src/lib.rs:4:5
  |
4 | /     iter.then(async |x| Ok(x))
5 | |         .try_for_each_concurrent(8, async |_| { Ok(String::new()) })
  | |                                                                    ^- help: consider using a semicolon here: `;`
  | |____________________________________________________________________|
  |                                                                      expected `()`, found `TryForEachConcurrent<..., ..., ...>`
  |
  = note: expected unit type `()`
                found struct `TryForEachConcurrent<Then<impl Stream, ..., ...>, ..., ...>`
  = note: the full name for the type has been written to '/playground/target/debug/deps/playground-8c6566b9d438c411.long-type-6263624529938574077.txt'
  = note: consider using `--verbose` to print the full type name to the console
Desired output
error[E0308]: mismatched types
 --> src/lib.rs:5:47
  |
5 |         .try_for_each_concurrent(8, async |_| { Ok(String::new()) })
  |                                                 -- ^^^^^^^^^^^^^ expected `()`, found `String`
  |                                                 |
  |                                                 arguments to this enum variant are incorrect
  |
 --> src/lib.rs:5:47
  |
5 |         .try_for_each_concurrent(8, async |_| { Ok(String::new()) })
  |                                                 ^^^-------------^
  |                                                    |
  |                                                    this argument influences the type of `Ok`
note: tuple variant defined here
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:561:5
    |
561 |     Ok(T),
    |     ^^
Rationale and extra context

The closures/futures do not need to be the same type, and attempting the dyn route will lead the user on a wild goose chase. The real error is what is being returned from the closure bodies.

In the less minimal example I'm including below, only the the first error is shown, so there is one relevant line out of 15.

The analogous code with Iterator and normal closures (instead of Stream and Future-returning errors) did not exhibit a misleading error in my (quick and unthorough) attempt: Playground.

Other cases
use anyhow::Error;
use futures::{self, Stream, StreamExt, TryStreamExt};
use futures::stream;
use std::pin::Pin;

type BoxStream<T> = Pin<Box<dyn Stream<Item = T>>>;
async fn inner_stream(_: u32) -> Result<BoxStream<Result<Vec<()>, Error>>, Error> {
    todo!()
}

pub async fn outer_stream(iter: impl Iterator<Item = String> + Send) -> Result<(), Error> {
    stream::iter(iter)
        .then(async |x| Ok::<_, Error>(x))
        .map_ok(async |_x| {
            stream::iter([].into_iter())
                .then(async |y| inner_stream(y).await)
                .try_flatten()
                .try_collect::<Vec<_>>()
        })
        .try_for_each_concurrent(8, async |x| {
            x.await
        })
        .await?;
    Ok(())
}
/*

error[E0308]: mismatched types
  --> src/lib.rs:20:47
   |
16 |                   .then(async |y| inner_stream(y).await)
   |                                   --------------------- the found `async` closure body
...
20 |           .try_for_each_concurrent(8, async |x| {
   |  _______________________________________________^
21 | |             x.await
22 | |         })
   | |_________^ expected `Result<(), anyhow::Error>`, found `TryCollect<TryFlatten<...>, ...>`
   |
   = note: expected `async` closure body `{async closure body@src/lib.rs:20:47: 22:10}` (`Result<(), anyhow::Error>`)
              found `async` closure body `{async closure body@src/lib.rs:20:47: 22:10}` (`TryCollect<TryFlatten<...>, ...>`)
   = note: no two async blocks, even if identical, have the same type
   = help: consider pinning your async block and casting it to a trait object

For more information about this error, try `rustc --explain E0308`.
*/
Rust Version
Rust Playground
Stable channel Build using the Stable version: 1.98.0
Beta channel Build using the Beta version: 1.99.0-beta.1 (2026-08-17 f47d5bb13648d5c859f5)
Nightly channel Build using the Nightly version: 1.100.0-nightly (2026-08-22 c54751567b19c4ceb08b)
Anything else?

Inspired by this URLO thread.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the minimal Rust Playground example and comparing the current and desired E0308 diagnostics. Trace the compiler diagnostic path for async closure return-type mismatches and add a regression test covering the reported case; done means the output points to the Result::Ok argument without the misleading async-closure type explanation.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.