主题
Rust 切片(Slice)完全理解指南
一次搞懂
&str、&[T]、生命周期和真实内存模型
如果说 Ownership(所有权) 是 Rust 的地基, 那 Slice(切片) 就是你每天都会踩在上面的“地板”。
很多新手在这里卡住的原因只有一个:
切片不是数据,而是“对数据的一段引用”
这篇文章只做一件事: 👉 让你从“内存视角”真正理解切片是什么
一、切片是什么?一句话版本
切片 = 对一段“连续内存”的只读引用视图
它有 4 个核心特性:
- ❌ 不拥有数据
- ❌ 不复制数据
- ✅ 指向原始数据的一部分
- ✅ 生命周期受原数据限制
Rust 中最常见的两种切片:
| 原始数据 | 切片类型 |
|---|---|
String / 字符串字面量 | &str |
数组 / Vec<T> | &[T] |
二、从一个最经典的例子开始
rust
let s = String::from("hello rust");
let part = &s[..5];这里发生了什么?
内存视角:
text
堆上的 String 数据:
┌─────────────────┐
│ h e l l o r u │
└─────────────────┘
↑
s 拥有整块内存
part = &s[..5]
└── 指向 [0..5) 这段内存重点:
part只是一个“窗口”- 数据仍然属于
s s一旦被释放,part立刻失效
三、为什么 &String 能当 &str 用?
rust
fn print_str(s: &str) {
println!("{}", s);
}
let s = String::from("hello");
print_str(&s);这背后是 Rust 的一个重要机制:
👉 Deref Coercion(自动解引用转换)
text
&String → &str等价于:
rust
print_str(s.as_str());设计哲学:
函数参数优先使用
&str,而不是&String
因为:
&str可以接收:String、字符串字面量、子切片- 更通用、更灵活
四、切片可以“再切片”
rust
fn first_word(s: &str) -> &str {
&s[..5]
}这里的逻辑是:
s是一个切片&s[..5]是对切片再切片- 返回值仍然是
&str
生命周期本质(编译器自动补全):
rust
fn first_word<'a>(s: &'a str) -> &'a str👉 返回值生命周期 ≤ 参数生命周期
五、字符串字面量为什么天然安全?
rust
let s = "hello rust";它的真实类型是:
rust
&'static str含义:
- 数据存放在程序只读区
- 程序运行期间永远有效
所以:
rust
fn print(s: &str) {}
print("hello");永远安全
六、数组切片:一模一样的模型
rust
let arr = [1, 2, 3, 4, 5];
let part = &arr[1..3];结果:
rust
part == &[2, 3]类型是:
rust
&[i32]字符串 vs 数组切片对照表
| 项目 | 字符串 | 数组 |
|---|---|---|
| 拥有者 | String | [T] / Vec<T> |
| 切片 | &str | &[T] |
| 内存 | UTF-8 字节 | 连续元素 |
| 是否零拷贝 | ✅ | ✅ |
七、⚠️ 最容易踩坑的点:字符串切片是“按字节”的
rust
let s = String::from("你好 Rust");
let part = &s[..2]; // ❌ 直接 panic原因:
- Rust 字符串是 UTF-8
- 中文字符占 3 个字节
- 切片必须落在 字符边界
正确做法(按字符):
rust
let result: String = s.chars().take(2).collect();👉 如果你不确定边界,宁可返回 String
八、什么时候该用切片?
✅ 适合用切片的场景
- 只读访问
- 函数参数
- 高性能(零拷贝)
- 临时视图
❌ 不适合用切片的场景
- 需要长期保存
- 需要修改
- 需要脱离原数据生命周期
九、终极总结(背这个就够了)
切片不是数据,而是“借用的一段连续内存视图”
- 没有所有权
- 生命周期受限
- 零拷贝
- 是 Rust 性能与安全并存的关键设计
理解切片 = 真正开始写“地道 Rust”