切片和数组有什么区别?

What is the difference between a slice and an array?

提问人:runrioter 提问时间:6/12/2015 最后编辑:Shepmasterrunrioter 更新时间:11/12/2023 访问量:10411

问:

为什么在这个例子中两者都是好的?&[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]

为什么可以胁迫?这种胁迫发生在什么其他情况下?&[T; n]&[T]

数组 切片

评论


答:

57赞 Chris Morgan 6/12/2015 #1

[T; n]是一个长度的数组,表示为相邻的实例。nnT

&[T; n]纯粹是对该数组的引用,表示为指向数据的细指针。

[T]是一个切片,一个未调整大小的类型;它只能通过某种形式的间接使用。

&[T],称为切片,是一种大小类型。它是一个胖指针,表示为指向第一项和切片长度的指针

因此,数组的长度在编译时是已知的,而切片长度是运行时的问题。数组目前在 Rust 中是二等公民,因为不可能形成数组泛型。有 、 、 &c. 的各种特征的手动实现,通常最多 32 个;由于此限制,切片通常更有用。可以胁迫的事实是使他们可以容忍的方面。[T; 0][T; 1]&[T; n]&[T]

有一个 for where implements 的实现,另一个 for where implements 的实现,以及 so as implements 的实现。fmt::Debug[T; 3]TDebug&TTfmt::Debugu8Debug&[u8; 3]

为什么可以胁迫?在 Rust 中,强制何时发生?&[T; n]&[T]

它会在需要时强制执行,而不是在其他任何时候。我能想到两种情况:

  1. 如果某物期待一个,而你给它一个,它就会默默地胁迫;&[T]&[T; n]
  2. 当你调用 时,它会观察到 上没有这样的方法,所以 autoref 开始发挥作用,它尝试 ,这无济于事,然后强制开始发挥作用,它尝试 ,它有一个叫做 的方法。x.starts_with(…)[T; n][T; n]&[T; n]&[T]starts_with

该代码段演示了这两者。[1, 2, 3].starts_with(&[1, 2])

评论

0赞 Giedrius Kudelis 7/17/2019
关于胁迫,我有点困惑。例如,Pattern trait 是为 &[char] 实现的。我制作了一个变量 x = [' '] 并尝试使用 &x 作为模式(例如,将其传递给 String 上的 trim_matches),但这会导致编译器错误。我可以看到 x 的类型是 &[char, 1]。如果我尝试传递 &x[..],它会被强制进入 &[char],现在编译器会找到 Pattern 实现。我是否遗漏了什么,或者在寻找特征实现时没有检查这个 &[T; n] -> &[T] 强制?
0赞 Chris Morgan 7/29/2019
我说的可能并不完全正确,但这是我的理解:泛型实现有一个令人讨厌的习惯,即妨碍强制,因为它们本质上被列为比数组到切片强制更重要。它看到所有满足的类型都实现了该特征,因此停在那里,试图满足该特征绑定,而不是玩弄类型强制。对于 const 泛型,这可以通过手动提供 and/或实现 () 来解决。是否会是另一回事。FnMut(char) -> bool[char; N]&[char; N]impl<const N: usize>
19赞 kennytm 4/11/2017 #2

为什么可以胁迫?&[T; n]&[T]

另一个答案解释了为什么应该强制到,在这里我将解释编译器如何工作,可以强制到。&[T; n]&[T]&[T; n]&[T]

Rust 中有四种可能的强制:

  1. 传递。

    • 如果胁迫 和 胁迫 ,则 胁迫 。TUUVTV
  2. 指针减弱:

    • 消除可变性:→和→&mut T&T*mut T*const T
    • 转换为原始指针:→和→&mut T*mut T&T*const T
  3. Deref 特征

    • 如果 ,则强制通过方法T: Deref<Target = U>&T&Uderef()
    • (类似地,如果 ,则强制通过T: DerefMut&mut T&mut Uderef_mut())
  4. Unsize 特征

    • 如果是“指针类型”(例如、、等),则 ,则强制为 。Ptr&T*mut TBoxRcT: 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 实现了 CoerceUnsized,则将其识别为“指针类型”。实际规则表述为,“如果 T: CoerceUnsized<U>T 强制到 U”。Ptr<X>

强制执行的原因是规则 4:(a) 编译器为每个 生成实现,并且 (b) 引用是指针类型。使用这些,可以强制 .&[T; n]&[T]impl Unsize<[T]> for [T; n][T; n]&X&[T; n]&[T]

10赞 keroro520 3/8/2019 #3

我根据 kennytm 和 Chris Morgan 的回答创作了这张照片。它描述了各种概念:

enter image description here

评论

12赞 Chris Morgan 7/29/2019
n内存中不存在。对于切片,它位于胖指针中,但对于数组或数组引用,它仅存在于类型中,如 ;这一切都在编译时整理出来,根本不会在运行时出现。N
2赞 Cypress Frankenfeld 2/3/2022
否则,这是一个超级有用的插图!我希望更多的人在解释概念时使用视觉辅助工具。