Rust 中的切片和引用之间的关系是什么?

What is the relationship between slices and references in Rust?

我对 Rust 完全陌生(因为我昨天才开始研究它),并且正在学习“The Rust Programming Language”。我有点停留在第 4.2 章(参考和借用)/ 4.3(切片类型)上,我试图在继续之前巩固我对参考的初步理解。我是一名经验丰富的程序员,主要是 C++ 背景(我对几种语言非常熟悉,但 C++ 是我最熟悉的语言)。

考虑以下 Rust 代码:

let string_obj: String = String::from("My String");
let string_ref: &String = &string_obj;
let string_slice: &str = &string_obj[1..=5];

根据我的理解,从第一行开始,string_obj是一个存储在栈中的String类型的对象,它包含三个字段:(1)指向文本的指针“我的字符串”,分配在堆上,以 UTF-8 编码; (2) 一个值为9的长度字段; (3) 一个容量字段,其值 >= 9。这很简单。

从第二行开始,string_ref 是对 String 对象的不可变引用,它也存储在堆栈中,其中包含一个字段 - 指向 string_obj 的指针。这让我相信(抛开所有权规则、语义和其他我尚未了解的有关引用的内容),引用本质上是指向其他对象的指针。同样,非常简单。

这是让我有些头疼的第三行。从文档中可以看出,string_slice 是存储在堆栈中的 &str 类型的对象,包含两个字段:1) 指向文本“y Str”的指针,在文本中与 string_obj 关联的“我的字符串”。 2) 值为 5 的长度字段。

但是,至少从表面上看,&str 类型根据定义是对类型 str 对象的不可变引用。所以我的问题如下:

  1. 究竟是什么一个str,它在内存中是如何表示的?
  2. 如何 &str - reference 类型,我 认为 只是一个指针 - 包含两个字段(一个指针和长度)?
  3. Rust 通常如何知道在构造引用时要创建什么/多少字段? (因此程序员怎么知道的?)

切片是 Rust 中的原始类型,这意味着它们不一定必须遵循其他类型的语法规则。在这种情况下,str&str 是特殊的,并且经过了一些魔法处理。

str 类型实际上 不存在,因为您不能拥有拥有其内容的切片。要求我们拼写这种类型“&str”的原因是语法上的:& 提醒我们我们正在处理从其他地方借来的数据,它需要能够指定生命周期,例如:

fn example<'a>(x: &String, y: &'a String) -> &'a str {
    &y[..]
}

这也是必要的,这样我们就可以区分 immutably-borrowed 字符串切片 (&str) 和 mutably-borrowed 字符串切片 (&mut str)。 (尽管后者的用途有限,因此您不会经常看到它们。)

请注意,同样的事情也适用于数组切片。我们有像 [u8; 16] 这样的数组,我们有像 &[u8] 这样的切片,但我们并没有真正直接与 [u8] 交互。这里的可变变体 (&mut [u8]) 比字符串切片更有用。

What exactly is an str, and how is it represented in memory?

如上所述,str kind-of 本身并不存在。 &str 的布局正如您所怀疑的那样——一个指针和一个长度。

(str是切片实际引用的字符,是一个so-calleddynamically-sized type。一般情况下,&T是不存在的没有 T 来引用。在这种情况下它有点倒退,因为没有 &str 切片 str 就不存在。)

How does &str - a reference type, which I thought was simply a pointer - contain TWO fields (a pointer AND a length)?

作为原语,它是编译器处理的特例。

How does Rust know in general what / how many fields to create when constructing a reference? (and consequently how does the programmer know?)

如果它是一个 non-slice 引用,那么它要么是一个指针,要么什么都不是(如果引用本身可以被优化掉)。