切片和数组有什么区别?
What is the difference between a slice and an array?
为什么在这个例子中 &[u8]
和 &[u8; 3]
都可以?
fn main() {
let x: &[u8] = &[1u8, 2, 3];
println!("{:?}", x);
let y: &[u8; 3] = &[1u8, 2, 3];
println!("{:?}", y);
}
The fact that &[T; n]
can coerce to &[T]
is the aspect that makes them tolerable.
为什么&[T; n]
可以强制到&[T]
?在其他什么情况下会发生这种强制转换?
[T; n]
是一个长度为 n
的数组,表示为 n
个相邻的 T
个实例。
&[T; n]
纯粹是对该数组的引用,表示为指向数据的瘦指针。
[T]
是一个slice,一个unsized类型;它只能通过某种形式的间接使用。
&[T]
,称为切片,是一种大小类型。它是一个 胖指针 ,表示为指向第一项的指针和切片的长度。
因此,数组的长度在编译时已知,而切片长度是运行时的事情。数组目前在 Rust 中是第二 class 公民,因为不可能形成数组泛型。 [T; 0]
、[T; 1]
、&c. 的各种特征有手动实现,通常最多 32 个;由于这个限制,切片更普遍有用。 &[T; n]
可以强制到 &[T]
的事实是使他们可以容忍的方面。
对于 [T; 3]
有一个 fmt::Debug
的实现,其中 T
实现了 Debug
,对于 &T
有另一个实现,其中 T
实现了 fmt::Debug
,因此 u8
实现了 Debug
,&[u8; 3]
也实现了。
Why can &[T; n]
coerce to &[T]
? In Rust, when does coercion happen?
它会在需要时强制执行,其他时候不会强制执行。我可以想到两种情况:
- 有些东西需要一个
&[T]
而你给它一个 &[T; n]
它会默默地强制执行;
- 当你在
[T; n]
上调用 x.starts_with(…)
时,它会观察到 [T; n]
上没有这样的方法,因此 autoref 开始发挥作用并尝试 &[T; n]
,这没有帮助,然后强制开始发挥作用,它尝试 &[T]
,它有一个名为 starts_with
. 的方法
片段 [1, 2, 3].starts_with(&[1, 2])
展示了两者。
Why can &[T; n]
coerce to &[T]
?
另一个答案解释了为什么&[T; n]
应该强制转换为&[T]
,这里我将解释编译器如何计算出&[T; n]
可以 强制转换为 &[T]
。
有four possible coercions in Rust:
传递性。
- 如果
T
强制转换为 U
并且 U
强制转换为 V
,则 T
强制转换为 V
.
指针弱化:
- 去除可变性:
&mut T
→ &T
和 *mut T
→ *const T
- 转换为原始指针:
&mut T
→ *mut T
和 &T
→ *const T
-
- 如果
T: Deref<Target = U>
,则 &T
通过 deref()
方法 强制转换为 &U
- (类似地,如果
T: DerefMut
,则 &mut T
通过 deref_mut()
强制转换为 &mut U
)
-
如果 Ptr
是 "pointer type"(例如 &T
、*mut T
、Box
、Rc
等) , 和 T: Unsize<U>
, 然后 Ptr<T>
强制转换为 Ptr<U>
.
Unsize
特征自动实现:
[T; n]: Unsize<[T]>
T: Unsize<Trait>
其中 T: Trait
struct Foo<…> { …, field: T }: Unsize< struct Foo<…> { …, field: U }>
,前提是 T: Unsize<U>
(以及一些使编译器的工作更容易的条件)
(Rust 将 Ptr<X>
识别为 "pointer type",如果它实现 CoerceUnsized
。实际规则表述为,“if T: CoerceUnsized<U>
然后 T
强制转换为 U
”。)
&[T; n]
强制转换为 &[T]
的原因是规则 4:(a) 编译器为每个 [T; n]
生成实现 impl Unsize<[T]> for [T; n]
,以及 (b) 引用&X
是指针类型。使用这些,&[T; n]
可以 强制转换为 &[T]
.
我根据 kennytm 和 Chris Morgan 的回答创建了这张图片。它描述了各种概念:
为什么在这个例子中 &[u8]
和 &[u8; 3]
都可以?
fn main() {
let x: &[u8] = &[1u8, 2, 3];
println!("{:?}", x);
let y: &[u8; 3] = &[1u8, 2, 3];
println!("{:?}", y);
}
The fact that
&[T; n]
can coerce to&[T]
is the aspect that makes them tolerable.
为什么&[T; n]
可以强制到&[T]
?在其他什么情况下会发生这种强制转换?
[T; n]
是一个长度为 n
的数组,表示为 n
个相邻的 T
个实例。
&[T; n]
纯粹是对该数组的引用,表示为指向数据的瘦指针。
[T]
是一个slice,一个unsized类型;它只能通过某种形式的间接使用。
&[T]
,称为切片,是一种大小类型。它是一个 胖指针 ,表示为指向第一项的指针和切片的长度。
因此,数组的长度在编译时已知,而切片长度是运行时的事情。数组目前在 Rust 中是第二 class 公民,因为不可能形成数组泛型。 [T; 0]
、[T; 1]
、&c. 的各种特征有手动实现,通常最多 32 个;由于这个限制,切片更普遍有用。 &[T; n]
可以强制到 &[T]
的事实是使他们可以容忍的方面。
对于 [T; 3]
有一个 fmt::Debug
的实现,其中 T
实现了 Debug
,对于 &T
有另一个实现,其中 T
实现了 fmt::Debug
,因此 u8
实现了 Debug
,&[u8; 3]
也实现了。
Why can
&[T; n]
coerce to&[T]
? In Rust, when does coercion happen?
它会在需要时强制执行,其他时候不会强制执行。我可以想到两种情况:
- 有些东西需要一个
&[T]
而你给它一个&[T; n]
它会默默地强制执行; - 当你在
[T; n]
上调用x.starts_with(…)
时,它会观察到[T; n]
上没有这样的方法,因此 autoref 开始发挥作用并尝试&[T; n]
,这没有帮助,然后强制开始发挥作用,它尝试&[T]
,它有一个名为starts_with
. 的方法
片段 [1, 2, 3].starts_with(&[1, 2])
展示了两者。
Why can
&[T; n]
coerce to&[T]
?
另一个答案解释了为什么&[T; n]
应该强制转换为&[T]
,这里我将解释编译器如何计算出&[T; n]
可以 强制转换为 &[T]
。
有four possible coercions in Rust:
传递性。
- 如果
T
强制转换为U
并且U
强制转换为V
,则T
强制转换为V
.
- 如果
指针弱化:
- 去除可变性:
&mut T
→&T
和*mut T
→*const T
- 转换为原始指针:
&mut T
→*mut T
和&T
→*const T
- 去除可变性:
-
- 如果
T: Deref<Target = U>
,则&T
通过deref()
方法 强制转换为 - (类似地,如果
T: DerefMut
,则&mut T
通过deref_mut()
强制转换为&mut U
)
&U
- 如果
-
如果
Ptr
是 "pointer type"(例如&T
、*mut T
、Box
、Rc
等) , 和T: Unsize<U>
, 然后Ptr<T>
强制转换为Ptr<U>
.Unsize
特征自动实现:[T; n]: Unsize<[T]>
T: Unsize<Trait>
其中T: Trait
struct Foo<…> { …, field: T }: Unsize< struct Foo<…> { …, field: U }>
,前提是T: Unsize<U>
(以及一些使编译器的工作更容易的条件)
(Rust 将
Ptr<X>
识别为 "pointer type",如果它实现CoerceUnsized
。实际规则表述为,“ifT: CoerceUnsized<U>
然后T
强制转换为U
”。)
&[T; n]
强制转换为 &[T]
的原因是规则 4:(a) 编译器为每个 [T; n]
生成实现 impl Unsize<[T]> for [T; n]
,以及 (b) 引用&X
是指针类型。使用这些,&[T; n]
可以 强制转换为 &[T]
.
我根据 kennytm 和 Chris Morgan 的回答创建了这张图片。它描述了各种概念: