考虑以下示例:

use std::ops::Index;
use std::ops::RangeFull;

fn f<T: Index<RangeFull>>(x: T) {}

fn main() {
    let x: [i32; 4] = [0, 1, 2, 3];
    f(x);
}

调用f(x)时,出现错误:

error[E0277]: the type `[i32; 4]` cannot be indexed by `std::ops::RangeFull`
 --> src/main.rs:8:5
  |
8 |     f(x);
  |     ^ `[i32; 4]` cannot be indexed by `std::ops::RangeFull`
  |
  = help: the trait `std::ops::Index<std::ops::RangeFull>` is not implemented for `[i32; 4]`
note: required by `f`
 --> src/main.rs:4:1
  |
4 | fn f<T: Index<RangeFull>>(x: T) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我很迷惑。我显然可以写例如let y = x[..];。这不是意味着用x索引RangeFull吗?数组在这方面有什么特殊之处吗?

最佳答案

正如您在documentation for the primitive array type中看到的那样,Index<…>没有直接为数组实现。部分原因是目前不可能为所有阵列大小提供一揽子实现,但主要是因为没有必要。切片的实现对于大多数目的而言已经足够。

编译器将x[..]表达式转换为*std::ops::Index::index(&x, ..),然后根据usual method call semantics对其求值。由于没有针对数组的Index<RangeFull>的实现,因此编译器反复取消引用&x并在最后执行不定大小的强制转换,最终找到Index<RangeFull>[i32]的实现。

调用泛型函数(例如您的示例中的f())的过程与方法调用语义不同。编译器首先根据您传递的参数推断出T是什么。在这种情况下,T推断为[i32; 4]。在下一步中,编译器将验证T是否满足特征范围,并且由于不满足,您将收到一条错误消息。

如果我们想使您的代码正常工作,则需要确保将一个切片传递给f()。由于切片是未调整大小的,因此我们需要通过引用传递它,因此我们需要像这样定义f():

fn f<T: ?Sized + Index<RangeFull>>(_: &T) {}
?Sized是必需的,因为类型参数接收隐式的Sized绑定(bind)。在调用f()时,我们需要确保T实际上被推断为[i32]而不是[i32; 4]。为此,我们可以显式指定T
f::<[_]>(&x);

或在传递参数之前显式执行未调整大小的转换,因此编译器会推断出所需的类型:
f(&x as &[_]);
f(&x[..])

关于rust - 数组不能被RangeFull索引吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55925523/

10-10 18:34