What is the return type of the indexing operation?

2019-01-04 14:20发布

I am trying, quite unsuccessfully, to play around with slices.

I have reduced my first issue to:

fn at<'a, T>(slice: &'a [T], index: usize) -> &'a T {
    let item = slice[index];
    item
}

It is my expectation that the return type of slice[index] be a reference, given the documentation:

pub trait Index<Index> {
    type Output;
    fn index(&'a self, index: &Index) -> &'a <Self as Index<Index>>::Output;
//                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

However, the compiler gives me an error:

error[E0308]: mismatched types
 --> src/main.rs:3:5
  |
3 |     item
  |     ^^^^ expected reference, found type parameter
  |
  = note: expected type `&'a T`
             found type `T`

Which I interpret as meaning that the type of item does not match the return type of the function (I introduced item solely for debugging purpose, to split expression evaluation from return).

If I switch the return type to T, which is the type of item, I get another error message:

error[E0508]: cannot move out of type `[T]`, a non-copy slice
 --> src/main.rs:2:16
  |
2 |     let item = slice[index];
  |                ^^^^^^^^^^^^
  |                |
  |                cannot move out of here
  |                help: consider using a reference instead: `&slice[index]`

After tinkering a bit, I found two work-arounds:

fn at<'a, T>(slice: &'a [T], index: usize) -> &'a T {
    &slice[index]
//  ^
}

fn at<'a, T>(slice: &'a [T], index: usize) -> &'a T {
    let ref item = slice[index];
//      ^~~
    item
}

forcing the type to be a reference does the trick.

Why are these shenanigans necessary in the first place? Am I doing something wrong?

标签: rust
2条回答
Emotional °昔
2楼-- · 2019-01-04 14:42

No, you're doing everything correctly. While index() method does return a reference, when it is invoked in an indexing operation its result is dereferenced automatically. This is done so the indexing is more natural: in every language where some kind of indexing operator exists (mainly C and C++), it returns values themselves, not the references into containers.

In order to obtain a reference into the collection you have to either apply a reference operator explicitly (like in your first "workaround") or to use reference pattern (like in the second one).

查看更多
beautiful°
3楼-- · 2019-01-04 14:53

This is a bit of helpful ergonomics that the compiler does for you in order to make the code look a bit nicer.

The return value of the Index trait is a reference, but the compiler automatically inserts a dereference for you when you use the sugared syntax []. Most other languages would just return the item from the array (copying it or returning another reference to the object, whatever is appropriate).

Due to Rust's importance of move / copy semantics, you can't always make a copy a value, so in those cases, you will usually use a &:

let items = &[1u8, 2, 3, 4];

let a: u8 = items[0];
let a: u8 = *items.index(&0); // Equivalent of above

let b: &u8 = &items[0];
let b: &u8 = &*items.index(&0); // Equivalent of above

Note that the indexing value is also automatically taken by reference, similar to the automatic dereference.

查看更多
登录 后发表回答