定义
在 Rust 中,生命周期(lifetime)是用来描述引用(reference)有效期的一种概念。每个引用都有一个生命周期,表示该引用指向的数据在内存中的存活时间。生命周期用于在编译时检查引用是否有效,并避免出现空指针、野指针等内存安全问题。
生命周期通过单引号字符(')来表示,通常采用小写字母 a、b、c 等表示。生命周期参数一般写在函数、结构体、枚举等数据类型定义的左尖括号后面,并用尖括号括起来。例如:
1
2
3
|
fn foo<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
// function body here
}
|
这个例子中,foo 函数有两个生命周期参数,‘a 和 ‘b,它们用于描述 x 和 y 引用的有效期。‘a 表示 x 引用的生命周期,‘b 表示 y 引用的生命周期。
在 Rust 中,生命周期的主要作用是帮助编译器进行引用的内存管理和安全检查,避免出现悬垂指针、空指针、野指针等内存安全问题。生命周期检查是 Rust 语言的静态检查机制之一,编译器会在编译时检查引用是否有效,以避免程序在运行时出现内存错误。
使用lifetime的场景
在 Rust 中,必须写明生命周期的情况主要包括以下几种情况:
函数的输入参数或返回值是引用类型,且该引用类型可能有多个实例,这些引用实例之间可能存在生命周期的依赖关系。
在结构体中储存引用类型时,结构体和其中的引用之间可能存在生命周期的依赖关系。
在实现 trait 时,trait 中定义的方法参数或返回值是引用类型,且该引用类型可能有多个实例,这些引用实例之间可能存在生命周期的依赖关系。
在上述情况中,编译器需要知道引用的生命周期,以便确定引用是否有效,以及引用是否存在悬垂指针等安全问题。如果没有指定生命周期,编译器将无法检查这些安全问题并发出编译错误。
例如,以下是需要指定生命周期的示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
struct Foo<'a> {
x: &'a str,
y: &'a str,
}
impl<'a> Trait for Foo<'a> {
fn bar(&self, x: &'a str) -> &'a str {
// method body here
}
}
|
在这些例子中,我们需要指定生命周期参数来确保编译器可以正确地检查引用的有效性。
应用原则
Rust中lifetime(生命周期)是一个非常重要的概念,它与所有权(ownership)和借用(borrowing)密切相关。下面是rust lifetime的几大原则:
-
每个变量都有其生命周期
每个变量都有其生命周期,生命周期描述了变量存在的时间范围,即在哪里被创建,在哪里被销毁。如果一个变量超出了其生命周期,它将被认为是无效的,访问它将导致编译时错误。
-
生命周期的注解是可选的,但有时必需
Rust编译器可以根据变量的使用情况推断出生命周期,因此不必在每个变量或函数上都手动注明生命周期。然而,在某些情况下,注明生命周期是必需的,例如,当函数参数和返回值都是引用时,需要注明它们的生命周期以确保编译器能够验证引用的有效性。
-
生命周期遵循引用的作用域
引用的生命周期不能超过其所引用的变量的生命周期。在rust中,引用的作用域是定义该引用的语句块。因此,在同一作用域中创建的引用具有相同的生命周期。
-
生命周期参数具有’static生命周期的最长寿命
‘static生命周期表示整个程序的生命周期,即从程序启动到程序结束。具有’ static生命周期参数的引用可以跨越整个程序运行时间,它们是最长寿命的引用。
-
生命周期参数是泛型类型的一部分
与其他泛型类型一样,生命周期参数可以用于泛型函数和结构体。这样可以为函数和结构体中的引用类型指定生命周期参数,以确保它们的有效性。
总的来说,lifetime是rust中非常重要的概念,它使得编译器能够静态验证程序中的引用有效性,并防止出现运行时错误。
rust lifetime常见错误
引用的lifetime超期
具体分为以下几种情况:
引用的lifetime超过了它所引用的变量的lifetime
1
2
3
4
5
6
7
8
9
|
fn main() {
let r;
{
let x = 5;
r = &x; // 'x' does not live long enough
}
println!("r: {}", r);
}
|
代码运行结果
函数返回引用的lifetime超过了函数参数的引用lifetime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
fn main() {
let s1 = String::from("hello");
let result;
{
let s2 = String::from("world");
result = get_longest_string(&s1, &s2); // expected lifetime parameters do not have the same lifetime bounds
}
println!("The longest string is {}", result);
}
fn get_longest_string<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
|
代码运行结果
结构体的lifetime超过其结构成员的引用的lifetime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
struct Car<'a> {
engine: &'a str,
color: String,
}
fn main() {
let color = String::from("red");
let engine;
{
let v8 = String::from("v8");
let my_car = Car {
engine: &v8, // 'v8' does not live long enough
color: color,
};
engine = my_car.engine;
}
println!("The engine is {}", engine);
}
|
代码运行结果
在函数中返回一个指向局部变量的引用
返回函数定义的局部变量的引用
1
2
3
4
5
6
7
8
9
10
|
fn main() {
let result = get_reference();
println!("The value is {}", result);
}
fn get_reference<'a>() -> &'a i32 {
let n = 42;
&n // `n` does not live long enough
}
|
代码运行结果
返回函数参数变量的引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let result = get_longest_string(&s1, s2);
println!("The longest string is {}", result);
}
fn get_longest_string<'a>(x: &'a str, y: String) -> &'a str {
if x.len() > y.len() {
x
} else {
&y // borrowed value does not live long enough
}
}
|
代码运行结果
缺少lifetime
函数缺少lifetime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let result = get_longest_string(&s1, &s2);
println!("The longest string is {}", result);
println!("{} {}", s1, s2);
}
fn get_longest_string(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
|
代码运行结果
结构体缺少lifetime
1
2
3
4
5
6
7
8
9
10
|
struct Foo {
x: &str, // 缺少生命周期参数,编译错误
}
fn main() {
let s = "hello";
let f = Foo { x: s };
println!("{}", f.x);
}
|
代码运行结果
正确代码如下:
1
2
3
4
5
6
7
8
9
|
struct Foo<'a> {
x: &'a str, // 指定生命周期参数
}
fn main() {
let s = "hello";
let f = Foo { x: s };
println!("{}", f.x);
}
|