Description
A minimized example, derived from a chain of changes that a friend (who has just begun learning Rust) did.
Starting from this code (Playground link):
fn main() {
let x = "".chars().collect();
}
The current output is:
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let x = "".chars().collect();
| ^
|
help: consider giving `x` an explicit type
|
2 | let x: _ = "".chars().collect();
| +++
Now someone more familiar with Rust wouldn't just blindly add a : _
(since it doesn't lead to telling Rust anything new here, just would move the problem), but this friend is new to Rust, and has often found suggestions in the "help:" section to immediately provide fixes, so they add the : _
, making the code into this (Playground link):
fn main() {
let x: _ = "".chars().collect();
}
The current output for this is:
error[E0282]: type annotations needed
--> src/main.rs:2:27
|
2 | let x: _ = "".chars().collect();
| ^^^^^^^ cannot infer type of the type parameter `B` declared on the associated function `collect`
|
help: consider specifying the generic argument
|
2 | let x: _ = "".chars().collect::<B>();
| +++++
Since they aren't sure what this B
is doing, but they realize that that it is a type parameter, they decide "oh, Rust can infer types for me, I'll just let it infer it", and decide to add the turbofish with _
, i.e., ::<_>
, making the code into this (Playground link):
fn main() {
let x: _ = "".chars().collect::<_>();
}
Again any seasoned Rust programmer would instantly recognize that this is unhelpful, but nonetheless, this leads to the following output:
error[E0282]: type annotations needed
--> src/main.rs:2:27
|
2 | let x: _ = "".chars().collect::<_>();
| ^^^^^^^ cannot infer type of the type parameter `B` declared on the associated function `collect`
|
help: consider specifying the generic argument
|
2 | let x: _ = "".chars().collect::<_>();
| ~~~~~
At this point, we've reached a fixed-point, and the error message is copied over to chat-message-platform-of-choice, with an "excuse me what rust" message:
I help them understand what happened, and they fix the issue, and go on their way. However, this now seems like a point where diagnostics could be improved. (Btw, the original issue happened nested inside an iterator chain inside another iterator chain, and there was a type error along the way too, so lots more issues there; I've distilled it down to the above story just to remove all irrelevant details; also, while not in the above story, if instead of beginning with the : _
, someone added the turbofish first (i.e., let x = "".chars().collect::<_>();
), then too one hits the final fixed-point case)
What could be improved:
- If something is explicitly marked as "to be inferred" via the
_
, maybe something like "cannot infer type of the type parameterB
(here,_
) declared on the associated functioncollect
" or similar. Probably someone can come up with a better way to write that though, but there should be something that connects theB
. - If both "sides" are marked to be inferred (in this case, type of
x
and type parameter in the turbofish), and making any one of them explicit would be sufficient, having an error message that says this might help. - Adding a diagnostic that specifically points out such annotations in general would also potentially help. In particular, I don't see any situation where (verbatim)
::<_>
is useful; except maybe in this animation. Having a diagnostic that tells users would probably be good. Similarly for (verbatim): _
. (I hope what I am saying here makes sense: I'm saying, as an example,::<Vec<_>>
is fine, but::<_>
is not). - (Not 100% the goal of the above story, but still)
collect
could have a special diagnostic help message that mentions containers, rather than theB
which can be confusing to new users.