[!Tip] str 本质上只是描述字符串数据的布局和语义的一个抽象概念,也就是一个动态大小类型(DST),它本身并不直接出现在栈上或堆上,而是由其他类型(如引用 &strBox<str> 等)来“承载”实际的数据存储位置和生命周期。换句话说,str 定义了字符串数据的内部格式(UTF-8 编码的字节序列),而实际的存储和使用则依赖于包装它的其他类型。

切片类型(Dynamically Sized Types, DST)

  1. str
    • 说明str 本身是一个字符串切片(string slice)的类型,但它是一个动态大小类型,也就是说其长度在编译时未知。
    • 使用方式:通常不会直接使用裸 str,而是通过引用来使用,例如 &str
  2. [T]
    • 说明[T] 表示一个元素类型为 T 的切片,其长度同样是在运行时确定(动态大小),编译时无法确定具体元素数量。
    • 使用方式:一般通过引用(如 &[T])来使用。

注意:这两种类型都属于 DST(动态大小类型),不能单独存在于栈上,只能通过指针(如引用或 Box)来间接使用。


编译时大小已知的类型(Sized)

  1. &str
    • 说明&str 是一个对 str 的引用。虽然它引用的内容(str)是动态大小的,但 &str 本身是一个胖指针(fat pointer),包含了指向数据的指针和数据的长度。因此,其在栈上的大小是固定的,可以在编译时确定。
  2. String
    • 说明String 是一个拥有所有权的、可增长的字符串类型。它内部封装了一个指针、长度和容量,这三个部分在编译时的内存布局是固定的(例如在 64 位平台上通常为 3 个机器字)。
    • 特性:虽然 String 的数据存储在堆上,且内容大小可能变化,但 String 结构本身在栈上的大小是确定的。
  3. Vec<T>
    • 说明Vec<T> 是一个拥有所有权的、动态数组(vector)类型。与 String 类似,它内部也包含指针、长度和容量,因而其结构大小在编译时是固定的。
    • 特性:虽然存储的数据在堆上,实际的元素数量也可能变化,但 Vec<T> 的内存布局是固定的。

总结

  • 切片(slice)类型
    • str[T] 都是 DST,不能在栈上单独存在,只能通过指针(如 &str&[T])来使用,其大小在编译时不确定。
  • 编译时大小已知(Sized)的类型
    • &str:引用类型(fat pointer),大小在编译时确定。
    • StringVec<T>:虽然它们管理的数据在堆上且大小可变,但自身的结构(指针、长度、容量)在编译时是固定大小的。

图例

下面是一个简单的图示,展示了 &strstr 的关系:

         &str (fat pointer)
       ┌─────────────────────┐
       │  pointer (地址)     │─────────────┐
       │  length  (长度)     │             │
       └─────────────────────┘             │
                                           │
                                           ▼
                                  ┌─────────────────┐
                                  │     str         │
                                  │ (动态大小类型)  │
                                  │  一串 UTF-8 字节 │
                                  └─────────────────┘

说明:

  • str:是一个动态大小类型(DST),表示一段 UTF-8 编码的字符串数据,但其长度在编译时是未知的,不能直接在栈上存储。

  • &str:是对 str 的引用,它是一个胖指针(fat pointer),包含了实际数据的内存地址和数据长度,因此在编译时大小是固定的,可以安全地存放在栈上。

这个图示清晰地表明了:

  • &str 中保存着一个指向实际字符串数据(str)的指针和该数据的长度;

  • str 是一个实际的字符串数据切片,其长度只有在运行时才能确定。