我不明白切片和参考之间的区别

Raf*_*ael 5 terminology reference slice rust

我不明白切片和参考之间的区别。&String和 和有&str什么区别?我在网上读到一些东西,说引用是一个细指针,切片是一个胖指针,但我不知道,似乎无法找到这两个的意思。我知道切片可以强制转换为引用,但它是如何做到的?Deref特质是什么?

Opt*_*ach 12

在 Rust 中,切片是一个连续的不同长度的同种类型数据块。

这是什么意思?

  • [u8]是一片。在内存中,这是一个u8s块。切片本身就是数据。但很多时候,人们将其&[u8]称为切片。A&[u8]指向该数据块的指针。该指针包含两件事:指向数据本身的指针和数据的长度。由于它包含两个东西,因此它被称为胖指针。A&u8也是一个引用(在这种情况下 也可以被认为是一个指针*),但我们已经知道它指向的任何东西都将是一个单一的u8. 因此,它是一个瘦指针,因为它只有一个元素。

    您可以保证 a 中的所有数据[u8]都是 类型u8

    由于您[u8]只是定义为 type 的连续内存块u8,因此没有关于它有多大的编译时定义。因此,我们需要将其长度存储在指向它的指针中。我们也不能把它放在堆栈上(这意味着:我们不能有一个只是一个[u8] **的局部变量)。

扩展:

  • A[T]Ts 的一个切片。对于任何给定的T,只要T它本身是一个大小类型***,我们就可以想象一个类型[T]
  • Astr是一个字符串的切片。它保证是有效的 UTF-8 文本,这就是将它与[u8]. Rust 可以放弃有效的 UTF-8 保证,而只是将其他所有内容定义str[u8].

好吧,由于您不能在本地拥有切片****,您可能想知道我们如何创建切片。

答案是我们将数据放在已知大小的东西中,然后从中借用切片。

举个例子:

let my_array: [u32; 3] = [1, 2, 3];
Run Code Online (Sandbox Code Playgroud)

我们可以切片my_array[u32]像这样:

let my_slice: [u32] = my_array[..];
Run Code Online (Sandbox Code Playgroud)

但是因为我们不能拥有一个大小未知的局部变量,我们必须把它放在一个引用下:

let my_slice: &[u32] = &my_array[..];
Run Code Online (Sandbox Code Playgroud)

切片的要点在于,无论数据来自何处,它都是一种非常灵活(不包括生命周期)的处理连续数据块的方法。我可以很容易地创建my_array一个Vec<u8>,它是堆分配的,它仍然可以工作。

&String 和 &str 有什么区别?

&String是对整个字符串对象的引用。Rust 中的字符串对象本质上是一个Vec<u8>. AVec包含一个指向它“包含”的数据的指针,因此您&String可以将其视为&&str. 而且,这就是为什么我们可以执行以下任一操作:

let my_string: String = "Abc".to_string();

let my_str: &str = &my_string[..]; // As explained previously
// OR
let my_str: &str = &*my_string;
Run Code Online (Sandbox Code Playgroud)

对此的解释使我想到您的最后一个问题:

什么是 deref 特征?

Deref性状,是描述解除引用(性状*)运算符。正如你在上面看到的,我能够做到*my_string。那是因为Stringimplements Deref,它允许您取消引用String. 同样,我可以Vec<T>将 a取消引用为 a [T]

但是请注意,该Dereftrait 被用于更多的地方,而不仅仅是使用的地方*

let my_string: String = "Abc".to_string();

let my_str: &str = &my_string;
Run Code Online (Sandbox Code Playgroud)

如果我尝试将 type 的值分配到 type&T的位置&U,那么 Rust 将尝试取消引用 my T,次数与获取 a 所需的次数相同U,同时仍至少保留一个引用。同样,如果我有 a &&&&....&&&&T,并且我尝试将其分配给 a &&&&....&&&&U,它仍然可以工作。

称为 deref 强制:自动将 a&T转换为 a &U,其中一定数量的*T将导致 a U


  • *:原始指针*const T*mut T大小与引用相同,但被编译器视为不透明。编译器不保证原始指针后面的内容,或者它们甚至正确对齐。因此,取消引用它们是不安全的。但是由于Dereftrait 定义了一个deref安全的方法,解引用原始指针是特殊的,也不会自动完成。
  • **:这也包括其他动态大小的类型,例如 trait 对象和extern types。这也包括struct包含动态大小类型作为其最后一个成员的 s,虽然这些很难正确构造,但将来会随着CoerceUnsizedtrait变得更容易。extern type使用unsized_locals夜间功能可以使所有这些(除了s)无效,该功能允许使用动态大小的局部变量。
  • ***:大小类型是在编译时已知大小的所有类型。您可以通用地识别它们;给定一个类型TT的大小在编译时是已知的 if T: Sized。如果T: ?Sized,那么它的大小在编译时可能是未知的(这T: ?Sized是调用者最灵活的要求,因为它接受任何东西)。由于切片要求内部的数据是连续的,并且在大小和类型上是同质的,因此动态大小的类型 (Or !Sized) 不可能包含在切片、数组或 aVec<T>中并保持O(1)索引。虽然 Rust 可能会编写特殊代码来索引一组动态大小的类型,但目前还没有。
  • ****:你实际上可以拥有一个切片,它只需要在拥有它的指针下。例如,这可以是 aBox<[T]>或 a Rc<[T]>。这些将自己释放切片(Box删除Rc时为A ,删除所有强引用和弱引用时为 a Rc(删除所有强引用时调用该值的析构函数,但直到所有弱引用都被释放时才会释放内存)也不见了。))。

  • 本书将“&amp;[T]”称为切片,因为它对于刚刚接触该语言的初学者来说更容易理解。[切片的参考页](https://doc.rust-lang.org/stable/reference/types/slice.html) 指出_切片类型写为 `[T]`_,并且 [文档对于切片](https://doc.rust-lang.org/std/primitive.slice.html)将其描述为_连续序列的动态大小视图,`[T]`_。 (2认同)

ask*_*sky 8

什么是参考

引用就像 C 中的指针(代表内存位置),但引用永远不会无效*(即 null),并且不能对引用进行指针算术。Rust 的引用与 C++ 的引用非常相似。使用引用的一个重要动机是避免moveing 或cloneing 变量。假设您有一个计算向量总和的函数(注意:这是一个玩具示例,获取向量总和的正确方法是nums.iter().sum()

fn sum(nums: Vec<u32>) -> Option<u32> {
    if nums.len() == 0 {
        return None;
    }
    let mut sum = 0;
    for num in nums {
        sum += num;
    }
    Some(sum);
}
Run Code Online (Sandbox Code Playgroud)

该函数移动向量,因此之后无法使用。

let nums = vec!(1,2,3,4,5);
assert_eq!(sum(nums), 15);
assert_eq!(nums[0], 1); //<-- error, nums was moved when we calculated sum
Run Code Online (Sandbox Code Playgroud)

解决方案是传递对向量的引用

fn sum(nums: &Vec<u32>) -> Option<u32> {
...
}
Run Code Online (Sandbox Code Playgroud)
let nums = vec!(1,2,3,4,5);
assert_eq!(sum(&nums), 15);
assert_eq!(nums[0], 1); // <-- it works!
Run Code Online (Sandbox Code Playgroud)

什么是切片

切片是“对表示为指针和长度的[连续]内存块的视图”。它可以被认为是对数组(或类似数组的东西)的引用。Rust 安全保证的一部分是确保您不会访问超出数组末尾的元素。为了实现这一点,切片在内部表示为指针和长度。与不包含长度信息的指针相比,这很胖。与上面的 sum 示例类似,如果nums是一个数组,而不是 a Vec,您将传递一个切片到sum(),而不是数组本身。

字符串与字符串

A是utf-8str编码字符的数组,an是 utf-8 编码字符的切片。是utf-8 编码字符的a ,并且实现,这意味着 an 的行为很像(强制) an 。这类似于(Vec 实现)的行为方式&strStringVecStringDeref<Target=str>&String&str&Vec<u32>&[u32]Deref<Target=[T]>


* 除非因不安全的锈蚀而失效

  • PS 需要明确的是,切片也是参考。当您对类型“T”进行通用时,那么“&amp;T”_绝对_是一个引用,并且它_可能_也是一个切片。 (2认同)