Closure doesn't support 2d vector

fn main() {
    let v = vec![vec![1, 2], vec![3, 4]];

    //bad. cannot infer type
    let f3 = |i| println!("{:?}", v[i][0]);
    f3(0);

    //bad. cannot infer type
    let f4 = |i, j| println!("{:?}", v[i][j]);
    f4(0, 1);

    //good
    let f0 = |i| println!("{:?}", v[i]);
    f0(0);

    //good
    let f1 = |i| println!("{:?}", v[0][i]);
    f1(0);

    //good
    let f2 = |i, j| {
        let x: &Vec<i32> = &v[i];
        println!("{:?}", x[j]);
    };
    f2(0, 0);
}

Why can't the compile know the v in v[i][0] or v[i][j] is a defined variable?

As far as I can tell, the compiler is not sure about the type of the closure argument i. It could be usize, but it could also be a range. (Like, v[0..2] is a thing for example.)

The compiler sometimes wants to know a type of a variable exactly rather early, once the variable is used in a sufficiently complicated setting. The subsequent call to the closure is considered in some cases in your example; like for f0 and f1.... really closure type inference in Rust is sometimes a bit weird - I can't say I fully understand what's going on.

In any case, without being able to understand why, this is clearly a case of the compiler being unhappy about not knowing the type of i when indexing into the result of v[i] in the v[i][0] or v[i][j] expression; it doesn't want to "wait" for more type information from the following line anymore. The way to fix this is to just annotate the type, i.e. write

let f3 = |i: usize| println!("{:?}", v[i][0]);
f3(0);

let f4 = |i: usize, j| println!("{:?}", v[i][j]);
f4(0, 1);

Just printing v[i] apparently is something the compiler is fine with, so f0 works, the compiler is somehow patient enough in type inference to wait for the "i is a number" information (and hence ultimately i: usize) from the subsequent line.

Edit: I just remembered that method calls are a common case where the compiler does not like to wait any longer for upcoming type hints (for the receiver type), the reason probably being that it can't do method resolution/lookup until the receiver type (i.e. the self type) is known. Since v[i] is like an *v.index(i) method call the type of v in such an expression is the receiver type of a method call. In v[i] the receiver type is known (to be Vec<i32>) and the compiler seems to be fine with uncertain argument type (i) and uncertain return type. But in v[i][0] the uncertain/not-yet-known return type is used in a method call again, it rustc really wants to do that method resolution right then without looking ahead any further, so you get the error. That's my intuition of what might be going on.

1 Like

Thanks!
Indeed the i's type can be Range or usize. The compiler maybe want to know the type enough early.

    //good
    let f0 = |i: std::ops::Range<usize>| println!("{:?}", v[i][0]);
    f0(0..2);

    //good
    let f0 = |i: usize| println!("{:?}", v[i][0]);
    f0(0);

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.