索引操作的返回类型是什么?

15

我试图玩弄切片,但是一直不成功。

我将我的第一个问题简化为:

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

根据文档,我希望slice[index]的返回类型是引用。

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

然而,编译器给我报错了:
error[E0308]: mismatched types
 --> src/main.rs:3:5
  |
3 |     item
  |     ^^^^ expected reference, found type parameter
  |
  = note: expected type `&'a T`
             found type `T`

我理解这句话的意思是 item 的类型与函数的返回类型不匹配(我仅为调试目的引入了 item,以将表达式求值与返回分开)。

如果我将返回类型切换为 T,即 item 的类型,我会得到另一个错误消息:

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

经过一番尝试,我找到了两种解决方法:

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
}

将类型强制转换为引用就可以解决问题。

为什么首先需要这些花招? 我做错了什么吗?

2个回答

14

这是编译器为了让代码看起来更好而为你实现的一些有用的人体工程学。

Index trait 的返回值确实是一个引用,但当你使用简化的语法[]时,编译器会自动插入一个解引用。在大多数其他语言中,只会返回数组中的项目(复制它或返回另一个对象的引用,具体取决于情况)。

由于 Rust 重视移动/复制语义的重要性,因此您并不总是可以复制一个值,在这种情况下,通常会使用 &

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

请注意,索引值也会自动按引用进行处理,类似于自动取消引用。


1
啊!我知道了,原来是被“[]”所迷惑了!我没有考虑直接调用“index”,因为我没有预料到会有区别,即使我已经注意到用“T”调用“[]”会被转化成用“&T”调用“index”的差异。感谢您的说明,它真的很有帮助,能够更好地形象化事物。 - Matthieu M.

8
不,你的操作完全正确。虽然index()方法确实返回一个引用,但在索引操作中调用该方法时,其结果会自动解除引用。这样做是为了使索引更加自然:在任何存在某种索引运算符的语言中(主要是C和C++),它返回的都是值本身,而不是容器中的引用。
为了获得集合中的引用,你必须显式地应用引用运算符(就像你第一个“解决方法”中所做的那样),或者使用引用模式(就像第二个“解决方法”中所示)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接