切片和数组有啥区别?
Posted
技术标签:
【中文标题】切片和数组有啥区别?【英文标题】:What is the difference between a slice and an array?切片和数组有什么区别? 【发布时间】:2015-08-27 22:36:46 【问题描述】:为什么在这个例子中&[u8]
和&[u8; 3]
都可以?
fn main()
let x: &[u8] = &[1u8, 2, 3];
println!(":?", x);
let y: &[u8; 3] = &[1u8, 2, 3];
println!(":?", y);
&[T; n]
可以强制转换为&[T]
的事实是使它们可以容忍的方面。 — Chris Morgan
为什么&[T; n]
可以强制转换为&[T]
?在什么其他条件下会发生这种强制?
【问题讨论】:
【参考方案1】:[T; n]
是一个长度为n
的数组,表示为n
相邻的T
实例。
&[T; n]
纯粹是对该数组的引用,表示为指向数据的细指针。
[T]
是一个切片,一个无大小的类型;它只能通过某种形式的间接使用。
&[T]
,称为切片,是一个有大小的类型。这是一个胖指针,表示为指向第一项的指针和切片的长度。
因此,数组的长度在编译时是已知的,而切片长度是运行时的问题。数组目前在 Rust 中是二等公民,因为不可能形成数组泛型。 [T; 0]
、[T; 1]
、&c. 的各种特征都有手动实现,通常最多 32 个;由于这个限制,切片更普遍有用。 &[T; n]
可以强制转换为 &[T]
的事实是使它们可以容忍的方面。
对于[T; 3]
,有一个fmt::Debug
的实现,其中T
实现Debug
,另一个用于&T
,其中T
实现fmt::Debug
,以此类推u8
实现Debug
, &[u8; 3]
也可以。
为什么
&[T; n]
可以强制转换为&[T]
?在 Rust 中,强制何时发生?
它会在需要时强制执行,其他时候不会。我可以想到两种情况:
-
如果某些东西需要
&[T]
,而你给它一个&[T; n]
,它会默默地强制执行;
当您在[T; n]
上调用x.starts_with(…)
时,它会观察到[T; n]
上没有这样的方法,因此自动引用开始发挥作用并尝试&[T; n]
,这没有帮助,然后强制开始发挥作用,它会尝试 &[T]
,它有一个名为 starts_with
的方法。
sn-p [1, 2, 3].starts_with(&[1, 2])
演示了两者。
【讨论】:
关于强制,我有点困惑。例如,Pattern trait 是为 &[char] 实现的。我创建了一个变量 x = [' '] 并尝试使用 &x 作为模式(例如,将其传递给字符串上的 trim_matches),但这会导致编译器错误。我可以看到 x 的类型是 &[char, 1]。如果我尝试传递 &x[..] 它会被强制进入 &[char] ,现在编译器会找到 Pattern 实现。有什么我想念的还是这个 &[T; n] -> &[T] 在寻找 trait 实现时没有检查强制转换? 我说的可能不完全正确,但这是我的理解:泛型实现有一个讨厌的习惯,会妨碍强制,因为它们本质上被列为比 array-to 更重要-切片强制。它看到所有满足FnMut(char) -> bool
的类型都实现了该特征,因此停在那里,试图满足该特征绑定而不是玩弄类型强制。使用 const 泛型,这可以通过手动提供 [char; N]
和/或 &[char; N]
实现 (impl<const N: usize>
) 来解决。会不会是另外一回事。【参考方案2】:
为什么
&[T; n]
可以强制转换为&[T]
?
另一个答案解释了为什么&[T; n]
应该强制转换为&[T]
,这里我将解释编译器如何计算出&[T; n]
可以强制转换为@ 987654330@.
有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
Deref
trait:
T: Deref<Target = U>
,则&T
通过deref()
方法强制转换为&U
(同样,如果T: DerefMut
,则&mut T
通过deref_mut()
强制转换为&mut U
)
Unsize
trait:
如果Ptr
是“指针类型”(例如&T
、*mut T
、Box
、Rc
等)和T: Unsize<U>
,则Ptr<T>
强制转换为Ptr<U>
.
Unsize
trait 自动实现:
[T; n]: Unsize<[T]>
T: Unsize<Trait>
T: Trait
struct Foo<…> …, field: T : Unsize< struct Foo<…> …, field: U >
,前提是 T: Unsize<U>
(以及一些使编译器的工作更容易的条件)
(Rust 将 Ptr<X>
识别为“指针类型”,如果它实现了 CoerceUnsized
。实际规则表述为,“如果 T: CoerceUnsized<U>
则 T
强制转换为 U
”。)
&[T; n]
强制转换为&[T]
的原因是规则 4:(a) 编译器为每个 [T; n]
生成实现 impl Unsize<[T]> for [T; n]
,并且 (b) 引用 &X
是指针类型。使用这些,&[T; n]
可以强制转换为&[T]
。
【讨论】:
【参考方案3】:我根据 kennytm 和 Chris Morgan 的回答创作了这张照片。它描述了各种概念:
【讨论】:
n
在内存中不存在。对于切片,它在胖指针中,但对于数组或数组引用,它只存在于类型中,如N
;都是编译时整理出来的,运行时根本不出现。以上是关于切片和数组有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章