在 Rust 中,切片是由不同长度的同质类型数据组成的连续块。
这是什么意思?
-
[u8] 是一个切片。在内存中,这是一个u8s 的块。切片本身就是数据。但很多时候,人们将&[u8] 称为切片。 &[u8] 是一个指向该数据块的指针。该指针包含两件事:指向数据本身的指针和数据的长度。由于它包含两个东西,因此它被称为胖指针。 &u8 也是一个引用(也可以被认为是一个指针在这种情况下 *),但我们已经知道它指向的都是一个u8。因此,它是一个细指针,因为它只有一个元素。
您可以保证所有[u8] 中的数据属于u8 类型。
由于您的[u8] 刚刚被定义为u8 类型的连续内存块,因此没有关于它有多大的编译时定义。因此,我们需要将它的长度存储在一个指向它的指针中。我们也不能把它放在堆栈上(这意味着:我们不能有一个只是 [u8] ** 的局部变量)。
扩展:
-
[T] 是Ts 的一部分。对于任何给定的T,只要T 本身是一个大小的类型***,我们就可以想象一个类型[T]。
-
str 是字符串的一部分。它保证是有效的 UTF-8 文本,这就是将它与 [u8] 区分开来的一切。 Rust 可能已经抛弃了有效的 UTF-8 保证,只是将 str 中的所有其他内容定义为 [u8] 的一部分。
好吧,既然您不能在本地拥有切片****,您可能想知道我们如何创建切片。
答案是我们将数据放入已知大小的东西中,然后然后从中借用切片。
举个例子:
let my_array: [u32; 3] = [1, 2, 3];
我们可以像这样将my_array 分割成[u32]:
let my_slice: [u32] = my_array[..];
但是由于我们不能拥有一个大小未知的局部变量,我们必须将它放在一个引用之下:
let my_slice: &[u32] = &my_array[..];
切片的意义在于,它是一种处理连续数据块的非常灵活(排除生命周期)的方法,无论数据来自何处。我可以很容易地将my_array 变成Vec<u8>,它是堆分配的,它仍然可以工作。
&String 和 &str 有什么区别?
&String 是对整个字符串对象的引用。 Rust 中的字符串对象本质上是一个Vec<u8>。 Vec 包含指向它“包含”的数据的指针,因此您的 &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;
对此的解释让我想到了你的最后一个问题:
什么是 deref 特征?
Deref 特征是描述取消引用 (*) 运算符的特征。正如你在上面看到的,我能够做到*my_string。这是因为String 实现了Deref,它允许您取消引用String。同样,我可以将Vec<T> 取消引用为[T]。
但是请注意,Deref trait 不仅用于使用 * 的地方,还用于更多地方:
let my_string: String = "Abc".to_string();
let my_str: &str = &my_string;
如果我尝试将&T 类型的值分配给&U 类型的位置,那么Rust 将尝试取消引用我的T,次数与获得U 一样多,同时仍然至少保留一份参考资料。同样,如果我有一个&&&&....&&&&T,并且我尝试将它分配给&&&&....&&&&U,它仍然可以工作。
这称为 deref coercion:自动将 &T 转换为 &U,其中一定数量的 *T 会导致 U。
- *:原始指针
*const T 和*mut T 与引用大小相同,但编译器将其视为不透明。编译器不保证原始指针后面的内容,或者它们是否正确对齐。因此,取消引用它们是不安全的。但由于Deref trait 定义了一个安全的deref 方法,因此取消引用原始指针是特殊的,也不会自动完成。
- **:这也包括其他动态大小的类型,例如特征对象和
extern types。这也包括structs,它也包含一个动态大小的类型作为它们的最后一个成员,虽然这些很难正确构造,但在未来使用CoerceUnsized trait 会变得更容易。使用unsized_locals nightly 功能可以使所有这些(extern types 除外)无效,该功能允许使用动态大小的局部变量。
- ***:大小类型是在编译时已知大小的所有类型。您可以笼统地识别它们;给定类型
T,如果T: Sized,则T 的大小在编译时是已知的。如果T: ?Sized,那么它的大小在编译时可能是未知的(T: ?Sized 是调用者最灵活的要求,因为它接受任何东西)。由于切片要求内部数据是连续的,并且在大小和类型上是同质的,动态大小的类型(或!Sized)不可能包含在切片、数组或Vec<T> 中,并保持@ 987654394@ 索引。虽然 Rust 可能会编写特殊代码来索引一组动态大小的类型,但目前还没有。
- ****:您实际上可以拥有一个切片,它只需要位于拥有它的指针下。例如,这可以是
Box<[T]> 或Rc<[T]>。这些将自行取消分配切片(删除时为Box,删除Rc 的所有强引用和弱引用时为Rc(删除所有强引用时调用值的析构函数,但内存在所有弱引用都消失之前不会被释放。))。