文章
&'static str、&str、String等的区别
目录
下面从内存分配、所有权与借用、生命周期和常见使用场景几个维度来对比 &'static str、&str、String。#
1. 类型概念与内存分配#
| 类型 | 内存位置 | 可变性 | 所有权 |
| `&'static str` | 二进制文件区(静态区) | 不可变 | 不拥有所有权(指向常量) |
| `&str` | 可在栈或堆 | 不可变 | 不拥有所有权(借用) |
| `String` | 堆 | 可变 | 拥有所有权 |
&str- 本质是
(ptr: *const u8, len: usize)的字符串切片。 - 不持有内存,只是对已分配字符串的借用。
- 本质是
&'static str&str的子类型,生命周期标注为'static。- 通常来自编译时的字符串字面量,例如
let s: &'static str = "hello";。 - 字符串内容编译进可执行文件,整个程序运行期间都有效。
String- 堆上分配可增长的 UTF‑8 字符串缓冲区。
- 结构体内部包含指向堆内存的指针、长度和容量。
- 拥有其所指向的数据,超出作用域时会自动释放。
2. 所有权与借用#
let s1: String = String::from("foo"); // 拥有所有权
let s2: &str = &s1; // 不可变借用
let s3: &'static str = "bar"; // 静态借用,永不过期
String** → **&str可以随时借用整个字符串的只读视图:
fn print_str(s: &str) { println!("{}", s); } print_str(&s1); // 借用而非转移所有权
```
&str** → **String需要复制/克隆:
let owned: String = s2.to_string(); let owned2: String = String::from(s2);
```
&'static str永不失效,可安全地在全局存储和多线程场景中传递,不需担心借用过期。
3. 生命周期#
&'static str生命周期是整个程序运行期。适合 “常量文本” 或全局消息、日志前缀等。&str生命周期受限于所借用值的作用域。借用期间不能修改原值,也不能让原值先于借用结束而析构。String生命周期由其所有者(变量)决定,到作用域尾自动drop,安全管理堆内存。
4. 使用场景对比#
| 场景 | 推荐类型 | 说明 |
| 编译时已知的不变文本 | `&'static str` | 零开销、永不过期,如程序标题、固定消息、日志标签。 |
| 接收外部输入(文件/网络/用户) | `String` | 大小未知、需要 grow/shrink、需要所有权便于传递和修改。 |
| 函数参数只需读取字符串 | `&str` | 通用且灵活,既能接收 `String` 也能接收字符串字面量。 |
| 需要拼接、多次修改或动态构造文本 | `String` | 支持 `.push_str()`, `.insert()`, `.replace_range()` 等可变操作。 |
| 暂时借用某段连续内存做切片 | `&str` | 从一长串文本中取子串无需复制,如 `full[5..10]`。 |
| 存储到结构体字段,需跨函数传递 | `String` 或 `&'static str` | 动态文本用 `String`,常量文本可用 `&'static str`,切勿用临时 `&str`。 |
5. 性能与内存考量#
- 零拷贝借用(
&str/&'static str)- 只有指针和长度的开销。
- 读性能最高,没有堆分配或复制。
- 堆分配(
String)- 首次分配需申请堆内存;
- 扩容时可能重新分配并复制已有数据;
- 适合可变文本,但要尽量复用同一个
String(避免反复to_string())。
6. 小结#
&'static str:零开销常量,全程有效,适合程序静态文本。&str:灵活借用,只读通用接口,适合作为函数参数。String:可变拥有,支持动态构造与修改,适合可变/外部文本。 通过合理选择,你可以在 性能(零拷贝)和 灵活性(动态拼接与所有权)之间取得最佳平衡。