请考虑以下示例:
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
并在最后执行unsized强制,最终找到Index<RangeFull>
的[i32]
的实现。
调用泛型函数的过程(如示例中的f()
)与方法调用语义不同。编译器首先根据您传递的参数推断T
是什么;在这种情况下,T
被推断为[i32; 4]
。在下一步中,编译器会验证T
是否满足特征边界,并且由于它没有,因此会收到错误消息。
如果我们想让你的代码工作,我们需要确保将切片传递给f()
。由于切片是未定义的,我们需要通过引用传递它,因此我们需要像这样定义f()
:
fn f<T: ?Sized + Index<RangeFull>>(_: &T) {}
?Sized
是必要的,因为类型参数接收隐含的Sized
界限。在调用f()
时,我们需要确保T
实际上被推断为[i32]
而不是[i32; 4]
。为此,我们可以明确指定T
f::<[_]>(&x);
或者在传递参数之前显式执行unsized转换,因此编译器会推断出所需的类型:
f(&x as &[_]);
f(&x[..])