When running cargo check this fails to compile with following error:
Checking type-annotation-mre v0.1.0 (/home/akrauze/scratch/type-annotation-mre)
error[E0282]: type annotations needed for `Vec<_>`
--> src/lib.rs:11:13
|
11 | let mut bars = vec![];
| ^^^^^^^^
12 |
13 | if let Some(_) = bars.iter().find(|&item| item.value == bar.value) {
| ---------- type must be known at this point
|
help: consider giving `bars` an explicit type, where the type for type parameter `T` is specified
|
11 | let mut bars: Vec<_> = vec![];
| ++++++++
For more information about this error, try `rustc --explain E0282`.
error: could not compile `type-annotation-mre` (lib) due to 1 previous error
I find this very odd, because:
it should be possible for the compiler to infer type or bars thanks to bars.push(bar) call
rust-analyzer shows correct types inline
I've tried running this with a couple of rust versions and editions, but the only things that changed were wording of error message.
Is this known limitation of the inference system in the compiler? I don't recall encountering anything similar in the past. If so, is it technically solvable (I suspect it is since rust-analyzer can infer types properly) and is it planned in the future?
Or is it some regression/bug in the compiler that should be reported?
it has always been like this. a short explanation is: type inference don't "look-ahead" when you access a field or call a method on a value of unknown type.
if you don't access the .value field, then it doesn't need to know the type at the time, something like this should compile:
#[derive(PartialEq, Eq)]
struct Bar {
value: u32,
}
// at this point, the inference just need some type `T: PartialEq<Bar>`
// which can be further resolved later
if bars.ite.find(|&item| item == &bar).is_some() {
unreachable!();
}
see also this reply from a recent thread, although that thread was about HashMap instead of Vec.
Thank you. Now it does make sense. In my use-case Bar struct has more fields, but I want to compare based only one one of them. So probably the simplest solution will be to add type annotation to the argument of the closure that is passed as a predicate.
As a developer I prefer it annotated. Otherwise (although one has type-hints today) one would need to scroll (potentially a lot) to see what the type of that item was.
In my view, it's best to simply annotate it: let mut bars:Vec<Bar> = vec![ ]; then the code compiles without error.
But I understand this isn't what you (or many others) expected/wanted.
you can also see why (and how) the PartialEq example would work (pseudo code):
// `T` is unknown in `Vec<T>`, a.k.a. an inference variable
let bars: Vec<T> = vec![];
// `vec::Iter` carries the variable `T`
let iter: vec::Iter<Item = &T> = bars.iter();
// the synthensized type of the predicate closure,
// inferred from the body, without any annotation:
// argument is in the shape of `&U` because of the pattern `&item`
let predicate: impl<U> FnMut(&U) -> bool where U: PartialEq<&Bar>
= |&item: &U| item == &bar;
// per the signature of `Iterator::find`:
// argument of the predicate, `&U`, is unified with `&Iter::Item`
// i.e.: &U == &Iter::Item == &&T
let maybe_found: Option<&T> = iter.find(predicate);
// per the signature of `Vec<T>::push()`: T == Bar
bars.push(bar);
// the solution based on inference rules:
// T == Bar
// U == &T == &Bar,
// and then, `U: PartialEq<&Bar>` checks
note, this is for illustration only. in reality, the closure is defined inline the call to Iterator::find() and some unification is done before checking the closure's body, which makes the annotation bars: Vec<Bar> work. but you can also annotate the closure instead:
if bars.iter().find(|item: &&Bar| item.value == bar.value).is_some() {
//...
}