生命周期初步概念
为什么需要生命周期?
假设有以下代码:
fn main() {
let r;
{
let x = 5; // x的作用域在内部块
r = &x; // r试图借用x
} // x离开作用域,被释放
println!("r = {}", r); // 错误!r成为悬垂引用
}
- 编译错误:
borrowed value does not live long enough - 根本问题:
r(引用)的生命周期不能超过它指向的数据(x)的生命周期
生命周期标注语法
-
基本形式
生命周期参数以'开头,通常用短名称如'a:&i32 // 普通引用 &'a i32 // 带有显式生命周期的引用 &'a mut i32 // 可变引用带生命周期 -
结构体中的生命周期
如果结构体包含引用,必须标注生命周期:struct Book<'a> { // 表示结构体的生命周期不超过字段`title`的生命周期 title: &'a str, } fn main() { let title = String::from("Rust Book"); let book = Book { title: &title }; // title的生命周期必须长于book // 如果title被提前释放,book.title会无效 }
函数中的生命周期标注
示例:返回两个字符串切片中较长的
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() { s1 } else { s2 }
}
fn main() {
let s1 = String::from("short");
let result;
{
let s2 = String::from("very long string");
result = longest(s1.as_str(), s2.as_str());
// 此时result的生命周期取决于s1和s2中更短的那个
println!("最长的字符串是: {}", result); // 正确:此时s2仍有效
}
// println!("{}", result); // 错误!s2已释放,result成为悬垂引用
}
生命周期标注的含义
longest<'a>:声明一个生命周期参数'as1: &'a str,s2: &'a str:两个参数必须具有相同生命周期'a-> &'a str:返回值的生命周期与参数相同- 核心规则:返回值的生命周期不能超过输入参数的生命周期
生命周期省略规则
在简单场景下,编译器可以自动推断生命周期,无需手动标注。例如:
// 以下情况可省略生命周期标注
fn first_word(s: &str) -> &str { // 实际等价于 first_word<'a>(s: &'a str) -> &'a str
// ...
}
编译器根据三个规则自动推断:
- 每个输入引用自动获得一个生命周期参数
- 如果只有一个输入生命周期,它被赋予所有输出生命周期
- 如果是方法(
&self或&mut self),输出生命周期与self的生命周期一致
练习题
-
修复结构体错误
以下代码为何报错?如何修复?struct Excerpt<'a> { part: &'a str, } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().unwrap(); let excerpt = Excerpt { part: first_sentence }; // 必须确保novel的生命周期长于excerpt } -
实现一个返回自身字段的函数
为Book结构体添加方法get_title,返回title的引用:impl<'a> Book<'a> { fn get_title(&self) -> &str { self.title } } -
挑战:跨作用域的生命周期
编写函数create_excerpt,从字符串切片生成Excerpt结构体:fn create_excerpt<'a>(text: &'a str, delimiter: char) -> Excerpt<'a> { let part = text.split(delimiter).next().unwrap(); Excerpt { part } }
常见错误分析
错误示例:
fn get_str() -> &str { // 缺少生命周期标注
let s = String::from("hello");
&s // 返回局部变量的引用(危险!)
}
- 错误信息:
missing lifetime specifier - 修复:不能返回局部变量的引用!必须确保返回的引用指向有效数据。