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?
}
```

From the reference *(plus some emphasis)*:

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.

1 Like

Thank you so much for such a clear explanation.

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]`

.

2 Likes

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.)

2 Likes

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.