[!Tip]
str本质上只是描述字符串数据的布局和语义的一个抽象概念,也就是一个动态大小类型(DST),它本身并不直接出现在栈上或堆上,而是由其他类型(如引用&str、Box<str>等)来“承载”实际的数据存储位置和生命周期。换句话说,str定义了字符串数据的内部格式(UTF-8 编码的字节序列),而实际的存储和使用则依赖于包装它的其他类型。
切片类型(Dynamically Sized Types, DST)
- str
- 说明:
str本身是一个字符串切片(string slice)的类型,但它是一个动态大小类型,也就是说其长度在编译时未知。 - 使用方式:通常不会直接使用裸
str,而是通过引用来使用,例如&str。
- 说明:
- [T]
- 说明:
[T]表示一个元素类型为T的切片,其长度同样是在运行时确定(动态大小),编译时无法确定具体元素数量。 - 使用方式:一般通过引用(如
&[T])来使用。
- 说明:
注意:这两种类型都属于 DST(动态大小类型),不能单独存在于栈上,只能通过指针(如引用或 Box)来间接使用。
编译时大小已知的类型(Sized)
- &str
- 说明:
&str是一个对str的引用。虽然它引用的内容(str)是动态大小的,但&str本身是一个胖指针(fat pointer),包含了指向数据的指针和数据的长度。因此,其在栈上的大小是固定的,可以在编译时确定。
- 说明:
- String
- 说明:
String是一个拥有所有权的、可增长的字符串类型。它内部封装了一个指针、长度和容量,这三个部分在编译时的内存布局是固定的(例如在 64 位平台上通常为 3 个机器字)。 - 特性:虽然
String的数据存储在堆上,且内容大小可能变化,但String结构本身在栈上的大小是确定的。
- 说明:
Vec<T>- 说明:
Vec<T>是一个拥有所有权的、动态数组(vector)类型。与String类似,它内部也包含指针、长度和容量,因而其结构大小在编译时是固定的。 - 特性:虽然存储的数据在堆上,实际的元素数量也可能变化,但
Vec<T>的内存布局是固定的。
- 说明:
总结
- 切片(slice)类型:
str与[T]都是 DST,不能在栈上单独存在,只能通过指针(如&str或&[T])来使用,其大小在编译时不确定。
- 编译时大小已知(Sized)的类型:
&str:引用类型(fat pointer),大小在编译时确定。String和Vec<T>:虽然它们管理的数据在堆上且大小可变,但自身的结构(指针、长度、容量)在编译时是固定大小的。
图例
下面是一个简单的图示,展示了 &str 与 str 的关系:
&str (fat pointer)
┌─────────────────────┐
│ pointer (地址) │─────────────┐
│ length (长度) │ │
└─────────────────────┘ │
│
▼
┌─────────────────┐
│ str │
│ (动态大小类型) │
│ 一串 UTF-8 字节 │
└─────────────────┘
说明:
-
str:是一个动态大小类型(DST),表示一段 UTF-8 编码的字符串数据,但其长度在编译时是未知的,不能直接在栈上存储。
-
&str:是对
str的引用,它是一个胖指针(fat pointer),包含了实际数据的内存地址和数据长度,因此在编译时大小是固定的,可以安全地存放在栈上。
这个图示清晰地表明了:
-
&str中保存着一个指向实际字符串数据(str)的指针和该数据的长度; -
str是一个实际的字符串数据切片,其长度只有在运行时才能确定。