引用有效期:生命周期入门
原理
生命周期(lifetime)是编译器对引用有效范围的静态标注,本质是 MIR 层的数据流分析结果。每一个引用均有一个隐式生命周期变量,编译器通过 borrow checker 求解生命周期的包含关系,确保所有引用始终指向有效数据。
'a 不会延长或缩短任何实际对象的存活时间(代码生成阶段完全消除),它只是一个编译期的”约束关系”。'static 生命周期表示数据在整个进程生命周期内有效(存放于只读数据段 .rodata),字符串字面量 "hello" 即 &'static str。
三条省略规则基于 Rust 团队对常见模式的统计分析设计,覆盖 90%+ 的场景:
- 每个引用参数获得独立生命周期
- 单个输入生命周期 → 返回值和它绑定
&self/&mut self→ 返回值和 self 绑定
违反规则时编译器在精确的工作点报错(NLL borrowck),而非泛泛的”生命周期错误”。
语法
悬垂引用(编译器阻止)
// let r;
// {
// let x = 5;
// r = &x;
// }
// println!("{}", r); // 编译错误:x does not live long enough函数中的生命周期标注
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 语义:返回值生命周期 = min(生命周期(x), 生命周期(y))fn first<'a>(x: &'a str, _y: &str) -> &'a str { x }
// y 可以不和返回值绑定结构体中的生命周期
struct Excerpt<'a> {
part: &'a str, // 字段引用需标注
}
impl<'a> Excerpt<'a> {
fn get(&self) -> &str { self.part } // 由规则3自动推断
}省略规则速查
| 场景 | 需要标注? |
|---|---|
fn f(s: &str) -> &str | 否(规则1+2) |
fn f(&self, s: &str) -> &str | 否(规则1+3) |
fn f(x: &str, y: &str) -> &str | 是(多输入,编译器无法确定) |
struct S { r: &str } | 是 |
fn f() -> &str | 通常不合理(悬垂风险) |
静态生命周期
let s: &'static str = "hello"; // 字符串字面量实践
力扣问题
力扣: 力扣字符串切片操作题 — 字符串切片操作
fn find_lcs<'a>(a: &'a str, b: &'a str) -> usize {
let a: Vec<char> = a.chars().collect();
let b: Vec<char> = b.chars().collect();
// LCS 算法
0
}AI 自检
'a标注在编译后会保留在二进制文件中吗?生命周期信息在哪个编译阶段被擦除?- NLL(Non-Lexical Lifetimes)解决了传统生命周期分析的什么问题?