What's the return type of index operation?

    let v = vec![1, 2];
    assert_eq!(&v[..], [1, 2]);

The index operation already returns reference.

#[stable(feature = "rust1", since = "1.0.0")]
impl<T, I> ops::Index<I> for [T]
where
    I: SliceIndex<[T]>,
{
    type Output = I::Output;

    #[inline]
    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}

So why we need & as trhe prefix of v[..]?

The trait method needs to return a reference so you don't move the value out of the container, but I believe the index operator's syntactic sugar was designed so that getting a reference to the i'th element has an explicit & (e.g. &v[2]) instead of it being implicitly a reference.

Indexing is just syntactic sugar (v[2] gets desugared to *v.index(2)) and can do whatever the language designers want, so I'm guessing they just went for what felt/looked most intuitive.

3 Likes

The index op already returns reference.

    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }

I don't understand why using & again,

The expression...

let x = v[2];

... Desugars to...

let x = *v.index(2);

... so if you want a reference to the 2'th item you need to add another & in front.

From the trait's documentation:

container[index] is actually syntactic sugar for *container.index(index) , but only when used as an immutable value. If a mutable value is requested, IndexMut is used instead. This allows nice things such as let value = v[index] if the type of value implements Copy .

4 Likes

Illustrating the desugaring vs. method call in code:

let v = vec![1, 2];
let indexed_slice: &[isize] = &v[..];
let returned_slice: &[isize] = v.index(..);
let indexed_value: isize = v[0];
let returned_value: isize = *v.index(0);

(Playground)

It's all about preserving lvalue-ness: if foo[index] returned a reference, you wouldn't actually get the item at index. Thus e.g. foo[index] = new_value wouldn't work, it would require an extra derferencing operator. In general, not getting the item but a reference to the item would be weird, but one can't just return the item if mutable indexing is to be supported. This is why the implicit dereference is present in the desugaring.

5 Likes

Could you clarify more here? IThanks.

Well, if we want to support mutating the element in-place, what would be the signature of IndexMut::index_mut()? It for sure can't return the element by-value, because how do you mutate it then? That's why it has to return a reference.

But thinking about it, even mutation isn't the only reason. Let's just think about the signature of Index::index(). If it returned the element by-value, then you couldn't ask for the address of that element (or you could, but it would be the address of a temporary), and you could only ever index into containers of Copy types, making the trait far less useful.