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] **的局部变量)。
扩展:
[T]是Ts 的一个切片。对于任何给定的T,只要T它本身是一个大小类型***,我们就可以想象一个类型[T]。str是一个字符串的切片。它保证是有效的 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安全的方法,解引用原始指针是特殊的,也不会自动完成。extern types。这也包括struct包含动态大小类型作为其最后一个成员的 s,虽然这些很难正确构造,但将来会随着CoerceUnsizedtrait变得更容易。extern type使用unsized_locals夜间功能可以使所有这些(除了s)无效,该功能允许使用动态大小的局部变量。T,T的大小在编译时是已知的 if T: Sized。如果T: ?Sized,那么它的大小在编译时可能是未知的(这T: ?Sized是调用者最灵活的要求,因为它接受任何东西)。由于切片要求内部的数据是连续的,并且在大小和类型上是同质的,因此动态大小的类型 (Or !Sized) 不可能包含在切片、数组或 aVec<T>中并保持O(1)索引。虽然 Rust 可能会编写特殊代码来索引一组动态大小的类型,但目前还没有。Box<[T]>或 a Rc<[T]>。这些将自己释放切片(Box删除Rc时为A ,删除所有强引用和弱引用时为 a Rc(删除所有强引用时调用该值的析构函数,但直到所有弱引用都被释放时才会释放内存)也不见了。))。引用就像 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]>
* 除非因不安全的锈蚀而失效