7.1 生命周期与借用深钻 🟢
生命周期 (Lifetimes) 是 Rust 编译器确保所有借用均为有效引用的方式。在 Rust 中,每个引用都有一个 生命周期,即该引用有效的范围。
1. 借用检查器 (Borrow Checker)
借用检查器会比较作用域(Scopes)以确定所有借用是否合法。
fn main() {
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // --+-- 'b |
r = &x; // | |
} // --+ |
// |
// println!("r: {r}"); // ❌ 错误:`x` 的寿命不够长
} // ---------+
2. 生命周期省略规则 (Lifetime Elision Rules)
在大多数情况下,你不需要显式编写生命周期标注,因为编译器遵循以下三条确定性规则:
- 规则 1:每个是引用的参数都会获得其各自的生命周期参数。
- 规则 2:如果恰好只有一个输入生命周期参数,则该生命周期会分配给所有输出生命周期参数。
- 规则 3:如果存在多个输入生命周期参数,但其中一个是
&self或&mut self,则self的生命周期会分配给所有输出生命周期参数。
3. 显式生命周期标注
有时编译器需要你的帮助来理解多个引用之间的关系。标注使用 ' 前缀(例如 'a)。
函数签名
#![allow(unused)]
fn main() {
// 这告诉编译器返回的引用至少与两个输入中寿命较短的那个一样长。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
}
结构体定义
如果结构体持有一个引用,则它 必须 显式地为该引用定义生命周期。
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("叫我以实玛利。几年前……");
let first_sentence = novel.split('.').next().expect("找不到 '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
}
4. 'static 生命周期
'static 是一个特殊的生命周期,它贯穿 程序运行的全过程。所有的字符串字面量都具有 'static 生命周期。
#![allow(unused)]
fn main() {
let s: &'static str = "我具有静态生命周期。";
}
对 C/C++ 开发者的总结
- 在 C/C++ 中:你需要手动跟踪指针的有效性。如果你弄错了,会导致段错误 (Segfault) 或未定义行为 (UB)。
- 在 Rust 中:你(有时)标注引用之间的关系。如果你弄错了,代码将 无法通过编译。