Strange type inference error


I stumbled upon a strange compile error today, that I have minimized to the following example:

#[derive(Copy, Clone, Debug)]
struct Element {
    next: Option<usize>,

fn main() {
    let array = [Element { next: None }; 16];
    let number: usize = 42;
    let mut option = None;

    match option {
        Some(n) => {
            // This alone compiles
            let bar = array[n];
            println!("Number is {:?}", bar);

            // The following doesn't compile:

            // error[E0282]: type annotations needed
            //  --> src/
            //    |
            // 19 |             let foo = array[n].next;
            //    |                       ^^^^^^^^^^^^^ cannot infer type for `_`

            let foo = array[n].next;
            println!("Number is {:?}", foo);
        None => option = Some(number),

    println!("{:?}", option);

What’s strange is that when I add a purposefully wrong type annotation like this:

let bar: bool = array[n];

the compiler correctly recognizes that bool is the wrong type and even tells me the correct type:

error[E0271]: type mismatch resolving `<usize as std::slice::SliceIndex<[Element]>>::Output == bool`
  --> src/
14 |             let bar: bool = array[n];
   |                             ^^^^^^^^ expected struct `Element`, found bool
   = note: expected type `Element`
              found type `bool`

However, as soon as I try to access a field, type inference fails. (Basically it knows that array[n] is of type Element, but later cannot infer the type of array[n].next.)

(If I move the None arm of the match up to the beginning, type inference succeeds…)

Is this expected behaviour?


It doesn’t know what option is:

let mut option = None;

The error surfaces in the wrong place, however.


I understood that much (took me some time, however, because I found the error confusing at first). Changing that line to
let mut option: Option<usize> = None;
made it compile.

However, what confuses me is that the line with bar compiles, whereas the one with foo doesn’t. The only difference is the additional field access…


My guess is it’s an artifact of how typeck + inference work (field access triggers typeck and it realizes it doesn’t have enough info here overall), and possibly a (known) bug/limitation. It’s definitely strange because it clearly allowed indexing the first time by inferring what n is.

File a github issue - it’s likely this is known, but you’ll get a more thorough explanation perhaps.


Thanks, I opened an issue on the Rust repo.