生命周期
生命周期(lifetime) 是 Rust 另一项独特机制:它标注引用的「有效范围」,让借用检查器确信返回的引用不会悬垂。
1. 为什么需要生命周期
当函数接收多个引用、返回一个引用时,编译器无法判断返回值的引用来自哪个参数:
// fn longest(x: &str, y: &str) -> &str { // 编译错误:缺少生命周期标注
// if x.len() > y.len() { x } else { y }
// }编译器不知道返回的引用该活多久——于是要求你显式标注。
2. 生命周期标注 'a
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let a = String::from("hello world");
let b = String::from("hi");
println!("较长的是: {}", longest(&a, &b));
println!("较长的是: {}", longest("abc", "abcdef"));
}'a 是一个名字(任意名称,习惯用 'a/'b)。含义是:返回的引用,活得不超过 x 与 y 中较短的那个。
ℹ️标注不改变生命周期长短
生命周期标注只是「告诉编译器约束关系」,不会延长或缩短任何数据的存活时间,纯粹是类型检查的信息。
3. 结构体中的生命周期
当结构体持有引用,必须标注其生命周期:
struct Excerpt<'a> {
part: &'a str, // 引用必须活得比结构体久
}
impl<'a> Excerpt<'a> {
// 规则3:&self 的生命周期赋给输出,无需手写标注
fn announce(&self, msg: &str) -> &str {
println!("注意: {}", msg);
self.part
}
}
fn main() {
let s = String::from("hello world");
let ex = Excerpt { part: &s[0..5] };
println!("{}", ex.part);
println!("{}", ex.announce("请阅读摘录"));
}⚠️结构体引用不能比数据活得久
若 s 在 ex 之前 drop,就悬垂了。编译器用 'a 强制「s 至少活到 ex 用完」。
4. 生命周期省略规则
多数函数不用手写标注,编译器按三条规则自动推导:
- 每个引用参数各有一个独立生命周期。
- 若只有一个输入生命周期,它赋给所有输出。
- 若有
&self/&mut self,其生命周期赋给所有输出。
fn first_word(s: &str) -> &str { ... } // 规则2:输入唯一,输出同生命周期5. 'static 生命周期
'static 表示「整个程序运行期都有效」。字符串字面量就是 &'static str;Box::leak 也能造 'static。
💡遇到 'static 报错
常见错误是把 &str 与 String 混用或返回局部引用。多数情况下你并不需要真正的 'static,标注正确输入生命周期即可。
🎯练习
实现 fn longest_with_default<'a>(x: &'a str, y: &'a str, ann: &str) -> &'a str,先打印 ann,再比较长度返回较长者。去掉标注看编译器如何提示你补上。
小结
- 生命周期标注
'a描述引用存活约束,不改变实际存活 - 返回引用且有多参数时,需显式标注
- 结构体含引用必须标生命周期
- 省略规则让常见情况免标注;
'static贯穿全程 - 下一章看闭包 →