Rust中的字符串类型:&str与String详解
一、&str的内存管理与生命周期
1. 内存管理机制
(1) 字符串字面量的&str
let s: &str = "hello"; // 字符串字面量
- 存储在静态只读内存区域
- 程序运行期间始终有效
- 不占用堆内存,仅包含指针和长度
(2) 堆分配字符串的切片引用
let s = String::from("hello");
let slice: &str = &s;
- 生命周期与源String绑定
- 当String被释放时切片自动失效
- Rust编译器防止悬垂引用
(3) 临时值的引用
let slice: &str = &String::from("hello"); // 编译错误示例
- 临时值在语句结束时释放
- 编译器会阻止此类不安全引用
2. 生命周期特性
- 静态生命周期:
&'static str - 动态绑定生命周期:依赖被引用数据的作用域
- 编译器通过生命周期标注确保引用有效性
3. 无需回收的原因
- 本质是数据的引用指针
- 不拥有数据所有权
- 内存管理由被引用数据负责
4. 生命周期示例
// 静态生命周期
fn static_str() -> &'static str {
"hello"
}
// 动态绑定示例
fn print_str(s: &str) {
println!("{}", s);
}
二、&str与String的核心区别
1. 基本特性对比
| 特性 | &str | String |
|---|---|---|
| 存储位置 | 栈/静态区 | 堆 |
| 可变性 | 不可变 | 可变 |
| 所有权 | 无所有权 | 拥有所有权 |
| 内存管理 | 引用计数 | 自动分配/释放 |
| 典型用途 | 函数参数/字面量 | 动态构建字符串 |
2. 代码示例对比
不可变性差异
// &str不可变示例
let s = "hello";
// s.push_str(" world"); // 编译错误
// String可变示例
let mut s = String::from("hello");
s.push_str(" world"); // 合法操作
所有权传递
fn take_ownership(s: String) {
println!("Got ownership of: {}", s);
}
fn borrow_str(s: &str) {
println!("Borrowed: {}", s);
}
3. 类型转换
String转&str
let s = String::from("hello");
let slice: &str = &s; // 自动转换
&str转String
let s = "hello";
let s1 = String::from(s);
let s2 = s.to_string();
4. 性能特征
- &str操作零内存分配
- String涉及堆内存管理
- 切片操作比克隆更高效
5. 使用场景建议
推荐使用&str的场景
- 函数参数传递
- 只读数据访问
- 模式匹配操作
- 组合字符串片段