Skip to content

Wrongly typing a collect with async streams causes confusing error messages alongside a cascade of unhelpful error messages #85132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Frederik-Baetens opened this issue May 10, 2021 · 2 comments
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Frederik-Baetens
Copy link

Frederik-Baetens commented May 10, 2021

Given the following code:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=05ab45beb0f9d4f2f7cdd9fc89f17a03

use futures::stream::{self, StreamExt};

#[tokio::main]
async fn main() {
    let key1: String = "/helloa".to_string();

    dbg!(&key1);

    let futlist = stream::iter(0..10);
    let _stringvec: Vec<String> = futlist.map(move |_| {
        async {
            key1.clone();
        }
    }).buffer_unordered(10).collect::<Vec<String>>().await;
}

The current output is:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Vec<String>: Extend<()>` is not satisfied
  --> src/main.rs:14:29
   |
14 |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   |                             ^^^^^^^ the trait `Extend<()>` is not implemented for `Vec<String>`
   |
   = help: the following implementations were found:
             <Vec<T, A> as Extend<&'a T>>
             <Vec<T, A> as Extend<T>>

error[E0277]: the trait bound `Vec<String>: Extend<()>` is not satisfied
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^ the trait `Extend<()>` is not implemented for `Vec<String>`
   |
   = help: the following implementations were found:
             <Vec<T, A> as Extend<&'a T>>
             <Vec<T, A> as Extend<T>>
   = note: required because of the requirements on the impl of `futures::Future` for `Collect<BufferUnordered<futures::stream::Map<futures::stream::Iter<std::ops::Range<{integer}>>, [closure@src/main.rs:10:47: 14:6]>>, Vec<String>>`
   = note: required by `futures::Future::poll`

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:9:9
   |
9  |     let futlist = stream::iter(0..10);
   |         ^^^^^^^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:10:35
   |
10 |     let _stringvec: Vec<String> = futlist.map(move |_| {
   |                                   ^^^^^^^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:10:47
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  _______________________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |_____^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |______^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |___________________________^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |____________________________________________________^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error[E0698]: type inside `async` block must be known in this context
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^ cannot infer type for type `{integer}`
   |
note: the type is part of the `async` block because of this `await`
  --> src/main.rs:10:35
   |
10 |       let _stringvec: Vec<String> = futlist.map(move |_| {
   |  ___________________________________^
11 | |         async {
12 | |             key1.clone();
13 | |         }
14 | |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   | |__________________________________________________________^

error: aborting due to 9 previous errors

Some errors have detailed explanations: E0277, E0698.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Ideally the output should look like:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Vec<String>: Extend<()>` is not satisfied
  --> src/main.rs:14:29
   |
14 |     }).buffer_unordered(10).collect::<Vec<String>>().await;
   |                             ^^^^^^^ the trait `Extend<()>` is not implemented for `Vec<String>`
   |
   = help: the following implementations were found:
             <Vec<T, A> as Extend<&'a T>>
             <Vec<T, A> as Extend<T>>

I guess just the first error shows what the problem is, but it's still a bit scary for a newcomer. Perhaps a nice sentence like "Collect expected a stream of strings, but got a stream of unity types ()"

Even better would be a suggestion to remove the semicolon so that strings are returned, but I can understand that that analysis may be very complex, and it may be hard to avoid making suggestions that are not what the user intended.

The cascade is somewhat similar to #85131 but unlike with #85131, where the main error message is already decent, I think the main error could also be made a bit better here.

@Frederik-Baetens Frederik-Baetens added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 10, 2021
@csmoe csmoe added the A-async-await Area: Async & Await label May 10, 2021
@tmandry tmandry added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label May 14, 2021
@Aaron1011
Copy link
Member

Related: #73741

@Frederik-Baetens
Copy link
Author

Frederik-Baetens commented Feb 15, 2022

Resolving #73741 seems to have removed the extraneous error messages. Newer compilers have much cleaner output.

The error could still be improved to include the suggestion to "return"(?) something within the async block. The compiler could suggest removing the ";". If the analysis for such a suggestion is too complex to be feasible, feel free to close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants