Why does calling a method on a variable prevent Rust from inferring the type of the variable?
Based on known facts (see below), it fails to compile because:
- the type checker goes through the function in the order it was written,
- in
let example = Default::default();
,example
can be anything which implementsDefault
, - field accesses & method calls require a known type,
- "anything implementing
Default
" is not a known type.
I replaced some_method()
with a field access and it produces same error.
From Type inference depends on ordering (#42333):
use std::path::PathBuf; pub struct Thing { pub f1: PathBuf, } fn junk() -> Vec<Thing> { let mut things = Vec::new(); for x in vec![1, 2, 3] { if x == 2 { for thing in things.drain(..) { thing.f1.clone(); } return vec![] } things.push(Thing{f1: PathBuf::from(format!("/{}", x))}); } things } fn main() { junk(); }
This produces a compiler error with Rust 1.33.0:
error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point
You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).
Comment #1:
This is a known limitation of the in-order type-checker. While inference flows freely,
thing.f1.clone()
is checked beforethings.push(Thing {...})
so it isn't known thatthing: Thing
when you try to access thef1
field. We may in the future move away from this, but there are no immediate plans.
What's more important is comment #2:
What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.
I don't know the full answer and I have next to no knowledge of the internal workings of the Rust compiler, but here are some deductions I've come to from my experience with Rust.
Information about types in Rust can "flow backwards", but there are certain times when Rust needs to know (for absolute certain) the type of an expression. In these situations, it must "already" know the type, i.e. it will not continue to look forward.
From what I've seen, this situation is limited to method calls. I suspect it has something to do with the fact that methods can be implemented on traits, which substantially complicates things. I doubt that there are any traits in scope with a method named some_method
, but I think that whenever the Rust compiler encounters a method call it requires the type to already be known for certain.
You can see this happen a lot with method calls on types which implement traits, the most common being the collect
method on a type that implements the Iter
trait. You will be able to call collect
, but won't be able to call any methods on the result unless you specify the type.
So this works:
fn create_numbers(last_num: i32) -> Vec<i32> {
let x = (0..10).collect();
x
}
But this does not:
fn create_numbers(last_num: i32) -> Vec<i32> {
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x
}