Why does indexing an explicitly typed vector fail with a type inference error?

Viewed 400

In the code below, I generate a vector and then use it as content for a closure:

fn main() {
    let f = {
        let xs: Vec<(usize, usize)> = Vec::new();
        // populate xs
        move |i, j| xs[j].1 - xs[i].0
    };
    let x = f(1usize, 2usize);
}

Why this does code fail to compile with a type inference error although the vector is explicitly typed?

error[E0282]: type annotations needed
 --> src/main.rs:5:21
  |
5 |         move |i, j| xs[j].1 - xs[i].0
  |                     ^^^^^ cannot infer type
  |
  = note: type must be known at this point
1 Answers

The [i] syntax in Rust comes from implementing the std::ops::Index trait.

That trait looks like this:

pub trait Index<Idx> 
where
    Idx: ?Sized, 
{
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

You can implement Index for a type multiple times, each with a different type for the Idx parameter. Vec supports as many different indexing mechanisms as possible by using a blanket implementation of Index:

impl<T, I> Index<I> for Vec<T>
where
    I: SliceIndex<[T]>, 

This will work for any type that also has a SliceIndex implementation, which includes usize, as you were trying to use, but also range types, like Range<usize> (e.g. 0..5) and RangeFrom<usize> (e.g. 0..). Inside the closure, the compiler doesn't know which implementation of Index is going to be used, and each possibility could have a different Output type, which is why it can't infer a single type there.

You can fix it by annotating the arguments of the closure:

let f = {
    let xs: Vec<(usize, usize)> = Vec::new();
    //
    move |i: usize, j: usize| xs[j].1 - xs[i].0
};
let x = f(1, 2);
Related