rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
94.9k stars 12.23k forks source link

E0191: "help: specify the associated type" suggested where this would be wrong #91997

Open jendrikw opened 2 years ago

jendrikw commented 2 years ago

Given the following code: [play](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&code=trait%20MyIterator%20%3A%20Iterator%20%7B%7D%0A%0Afn%20main()%20%7B%0A%20%20%20%20let%20_%20%3D%20MyIterator%3A%3Anext%3B%0A%20%20%20%20%2F%2Flet%20_%20%3D%20MyIterator%3CItem%3D()%3E%3A%3Anext%3B%0A%7D)

trait MyIterator : Iterator {}

fn main() {
    let _ = MyIterator::next;
    //let _ = MyIterator<Item=()>::next;
}

The current output is:

error[E0191]: the value of the associated type `Item` (from trait `Iterator`) must be specified
 --> src/main.rs:4:13
  |
4 |     let _ = MyIterator::next;
  |             ^^^^^^^^^^ help: specify the associated type: `MyIterator<Item = Type>`

But when when I write let _ = MyIterator<Item=()>::next;, it gets interpreted as comparison expressions like (MyIterator < Item) = (() > ::next) and it fails to compile with a lot of "cannot find value Item in this scope" and "cannot find crate next in the list of imported crates". The suggestion is confusing because it is unclear how to apply it.

The error goes away if you use Iterator directly: let _ = Iterator::next; produces the error one would expect:

error[E0283]: type annotations needed for `for<'r> fn(&'r mut Self) -> Option<<Self as Iterator>::Item>`
  --> src/main.rs:2:13
   |
2  |     let _ = Iterator::next;
   |         -   ^^^^^^^^^^^^^^ cannot infer type
   |         |
   |         consider giving this pattern the explicit type `for<'r> fn(&'r mut Self) -> Option<<Self as Iterator>::Item>`, with the type parameters specified
   |
   = note: cannot satisfy `_: Iterator`
note: required by `std::iter::Iterator::next`
BGR360 commented 2 years ago

I think it gets evaluated as an expression because you are missing a ::. If you add them, then you get a new error message (play):

trait MyIterator : Iterator {}

fn main() {
    let _ = MyIterator::<Item=()>::next;
}
error[E0783]: trait objects without an explicit `dyn` are deprecated
 --> src/main.rs:4:13
  |
4 |     let _ = MyIterator::<Item=()>::next;
  |             ^^^^^^^^^^^^^^^^^^^^^ help: use `dyn`: `<dyn MyIterator::<Item=()>>`
jendrikw commented 2 years ago

I see, let me rephrase the issue:

The suggestion is "help: specify the associated type: MyIterator<Item = Type>" where "help: specify the associated type: MyIterator::<Item = Type>" with a double colon would be correct.

fmease commented 3 weeks ago

Triage: Suggestion is correct for code using dyn (which all code written in Rust >=2021 uses). For code written without dyn in Rust <2021: No change. As mentioned, the suggestion should use an expression-style path (i.e., one with turbofishes).

Borgerr commented 1 week ago

@rustbot claim

Borgerr commented 1 week ago

Working on tests before creating a PR. Multiple tests exist already that have an odd hardcoded expectation for error output, so I'll be double checking those make sense with these changes, and adding a few tests of my own. Otherwise, this really does seem like it was a simple two character fix, unless I missed something somewhere.

fmease commented 1 week ago

Otherwise, this really does seem like it was a simple two character fix, unless I missed something somewhere.

Yeah, it's a pretty easy one. Adding :: before <...> is always correct — not only in expression contexts but also in type contexts since turbofish is also possible in type positions (e.g., fn f(_: Option::<u32>) {}) — albeit redundant.

https://github.com/rust-lang/rust/issues/91997#issuecomment-2168508882:

As mentioned, the suggestion should use an expression-style path

Omitting :: before <...> in type contexts would be a possible "optimization". However, it really isn't worth doing here. Consider the following code which is ill-formed even after specifying the assoc ty.

//@ edition: 2015
trait MyIterator: Iterator {}

fn main() {
    let _: MyIterator::Item;
}

MyIterator<Item = ()>::Item (<dyn MyIterator<Item = ()>>::Item) would only be well-formed if we defined an inherent associated type Item (#8995) on dyn MyIterator which doesn't work yet anyway (#106719) and is pretty niche) or once™ we support arbitrary shorthand projections (#22519).