Appearance
Rust 生命周期
生命周期是 Rust 中用于确保引用有效性的机制。本章节将介绍 Rust 中的生命周期概念。
生命周期的概念
生命周期是指引用有效的时间段。在 Rust 中,每个引用都有一个生命周期,编译器会检查引用的生命周期是否有效。
为什么需要生命周期
生命周期的主要目的是防止悬垂引用(dangling references):
rust
fn main() {
let r;
{
let x = 5;
r = &x; // 错误:x 的生命周期短于 r
}
// println!("r = {}", r); // x 已经离开作用域
}生命周期注解
生命周期注解使用撇号 ' 表示:
rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}这里的 'a 是一个生命周期参数,它表示 x、y 和返回值的生命周期是相同的。
生命周期省略规则
Rust 编译器有一套生命周期省略规则,可以在某些情况下省略生命周期注解:
每个参数的生命周期都有自己的生命周期参数:对于
fn foo(x: &T),会自动添加fn foo<'a>(x: &'a T)。如果只有一个输入生命周期参数,那么它会被赋给所有输出生命周期参数:对于
fn foo(x: &T) -> &U,会自动添加fn foo<'a>(x: &'a T) -> &'a U。如果有多个输入生命周期参数,但其中一个是
&self或&mut self,那么self的生命周期会被赋给所有输出生命周期参数:对于impl T { fn foo(&self) -> &U },会自动添加impl T { fn foo<'a>(&'a self) -> &'a U }。
结构体中的生命周期
当结构体包含引用时,需要为结构体添加生命周期注解:
rust
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
println!("Important excerpt: {}", i.part);
}方法中的生命周期
为结构体实现方法时,也需要考虑生命周期:
rust
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
println!("Level: {}", i.level());
println!("Announcement: {}", i.announce_and_return_part("Hello!"));
}静态生命周期
'static 是一个特殊的生命周期,表示引用可以在整个程序运行期间有效:
rust
fn main() {
let s: &'static str = "Hello, world!";
println!("s = {}", s);
}字符串字面量默认具有 'static 生命周期。
生命周期的实际应用
解决悬垂引用
rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let s1 = String::from("hello");
let result;
{
let s2 = String::from("world");
result = longest(&s1, &s2); // 错误:s2 的生命周期短于 result
}
// println!("Longest: {}", result);
}正确的使用方式
rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let result = longest(&s1, &s2);
println!("Longest: {}", result);
}总结
- 生命周期:引用有效的时间段
- 生命周期注解:使用
'a等符号表示 - 生命周期省略规则:编译器自动推断生命周期的规则
- 结构体中的生命周期:当结构体包含引用时需要添加生命周期注解
- 静态生命周期:
'static,表示引用可以在整个程序运行期间有效
生命周期是 Rust 类型系统的重要组成部分,它确保了引用的有效性,防止了悬垂引用等内存安全问题。通过本章节的学习,你应该已经理解了 Rust 中的生命周期概念和使用方法。