I have a basic question about understanding references to vectors:

fn main() {
let v: Vec<i32> = vec![1,2,3,4];
let third = &v[2];
// The book says that third gives a reference to the element.
// This is correct. assert_eq!(third, 3) fails but assert_eq!(third, &3)
// succeeds.
assert_eq!(third, &3);
let r = &v;
// r is a reference to v
// what is r[2]? Or, rather why is it 3?
// Either indexing a reference should make no sense.,
// or should r[2] be not the same as &v[2]?
assert_eq!(r[2], 3); // this succeeds, why?
}

Array and slice-typed values can be indexed by writing a square-bracket-enclosed expression of type usize (the index) after them. When the array is mutable, the resulting memory location can be assigned to.

For other types an index expression a[b] is equivalent to *std::ops::Index::index(&a, b), or *std::ops::IndexMut::index_mut(&mut a, b) in a mutable place expression context. Just as with methods, Rust will also insert dereference operations on a repeatedly to find an implementation.

Based on the fact that Vec<T>implements the Index<â€¦> trait, but references donâ€™t, this means that r[2] will desugar to *Index::index(&*r) with the extra dereference * in front of the r that makes &*r be of type &Vec<i32>, not &&Vec<i32> and thus match the &self argument type of the implementation Index<usize> for Vec<i32>.

The Index::index call then returns a &i32 reference to the element, and the desugaring makes sure to dereference this result (see the â€ś*â€ť at the very beginning) so that r[2] is of type i32, and youâ€™d need to write &r[2] if you wanted a &i32. Feel free to ask follow-up questions if any of this explanation is confusing or otherwise not clear to you.

By the way, the remark â€śjust as with methodsâ€ť hints at how methods have a similar feature (even a bit more powerful, e.g. also allowing references to be added) as documented here.

Explained in a different way, it has to do with operator precedence. The reference tells us that indexing has a higher precedence than taking a reference. Therefore, &v[2] is equivalent to &(v[2]). In the second example, let r = &v; r[2] does the referencing first, so it's not equivalent to &v[2].

Oh, I didnâ€™t even catch that potential source of confusion, but with that pointed out, the comment

should r[2] be not the same as &v[2]

makes some more sense.

Indeed, the v[2] binds stronger, desugars to *Index::index(&v, 2), and then, as a consequence, the & added to the front makes &v[2] sugar for &*Index::index(&v, 2). The return type of this Index::index call is &i32; the dereference that comes from the desugaring means we get a i32 as the type of v[2] / *Index::index(&v, 2), but these are place expressions, so the actual place of the i32 in the vector in memory is not yet forgotten by the dereferencing alone, so that adding the & in front gives us a &i32 (pointing to the same place) again, as the type for &v[2] of &*Index::index(&v, 2).

(Other so-called â€śplace expression contextsâ€ť where the fact that v[2] refers to a place, not just a value, become relevant include e.g. assignment operators [though those also go through IndexMut, not Index], so that v[2] = 42 or v[2] += 1 work as expected.)

Thank you both. I was thinking of whether I could also make sense of this in terms of operator precedence and was going to ask today. So, thanks for clarifying that.