Rust 生命周期
生命周期标注如何描述引用之间的存活关系;省略规则与函数签名中的常见模式。
#concept
#growing
#dev
#rust
[!info] related notes
Rust 生命周期
这个概念为什么出现
引用不能比它指向的数据活得更久。多数时候编译器能推断;当函数有多个引用输入/输出时,需要你用生命周期标注说明输出借自哪路输入。
[!abstract] 一句话理解 生命周期是引用有效的作用域参数;
'a不创造生命,只描述借用关系,帮助编译器证明返回的引用不会悬垂。
最小可运行示例
场景:广告文案——返回两个候选里较长的借用
示例比较两个 slogan 的 UTF-8 字节长度,返回较长者的 &str,平局取第一个;这不等于可见字符数或显示宽度。
// 业务意图:在两个 slogan 中返回较长的借用。
// 教学点:显式 'a 说明返回引用与两个输入中的借用关系。
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
fn main() {
let s1 = String::from("flash-sale");
let s2 = String::from("vip-only-weekend");
let pick = longer(&s1, &s2);
println!("pick={pick}");
}
建议运行:cargo run
期望输出:
pick=vip-only-weekend
结合场景再看三个关注点
- 返回值生命周期不能长于任一可能来源(此签名要求两者同一
'a约束下的交集语义) - 标注是契约,写错会编译失败或过度限制
- 不需要标注局部变量的“内部推断” 时保持简洁
核心概念与准确模型
标注位置
fn f<'a>(x: &'a str) -> &'a str { x }
省略规则(直觉)
每个省略生命周期的输入引用获得独立参数;只有一个输入生命周期时,将它赋给省略的输出。方法若有 &self / &mut self,则省略的输出生命周期绑定接收者,即使还有其他引用参数。其余无法确定来源的引用输出需显式说明。
不是“延迟 drop 的魔法”
生命周期不改变运行时 drop 时机;它是静态证明。
结构体字段引用
struct Excerpt<'a> { part: &'a str }
结构体不能比其字段借用活得更久,见 进阶。
设计动机
- 零运行时开销的悬垂引用防护
- 让 API 明确“数据从哪来”
- 与泛型同一套参数语法
边界与误区
&'static T指向可在整个程序期间有效的数据;T: 'static只限制 T 不含短于 static 的借用,并不要求 T 永远不析构。例如拥有型 String 可以满足该 bound- 生命周期纠缠难读时,优先改为拥有型数据
- 与异步/自引用结构体是高级坑
[!warning] 常见误区:觉得加
'a能修好任何借用错误 标注只描述关系;所有权/作用域设计错误无法靠记号掩盖。
工程实践
- 返回已有数据的视图时优先表达真实借用关系;返回新生成数据或需要独立寿命时使用 String
- 生命周期标注不延长原数据寿命;别只为通过编译而复制数据或添加 static
- 公共 API 生命周期写清楚并文档化
- 复杂时拆函数降低标注难度
本节总结
- 生命周期描述借用关系
- 返回引用可来自输入或静态数据,不能借用即将销毁的局部拥有值
- 能拥有就简单,需借用再标注
自测题
- 生命周期参数是否影响运行时?
- 何时应该返回
String而不是&str?
参考答案
- 否,主要是编译期检查。
- 需要独立所有权、拼接新数据、或借用关系说不清时。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The Book — Validating References with Lifetimes | 官方书 | 生命周期 |
| Rust Reference — Lifetime elision | 规范 | 省略规则 |