编程语言生命周期-Rust
XIU生命周期是为了避免悬垂引用
了每一个引用&都有一个生命周期
悬垂引用
悬垂引用指的是一个引用指向的内存已经被释放或者不再有效,编程者如果在这个时候还尝试访问这个失效的引用就会发生错误
r 引用的值在尝试使用之前就离开了作用域

借用检查器
Rust 编译器有一个 借用检查器(borrow checker),它比较作用域来确保所有的借用都是有效的
r 的生命周期标记为 ‘a 并将 x 的生命周期标记为 ‘b
被引用者的生命周期要大于或者等于引用者的生命周期,或者直接将所有权交给引用者

函数中的泛型生命周期
编写一个返回两个字符串 slice 中较长者的函数
我们定义这个函数的时候,并不知道传递给函数的具体值,所以也不知道到底是 if 还是else 会被执行
1 2 3
| &i32 &'a i32 &'a mut i32
|
单个的生命周期注解本身没有多少意义,因为生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {result}"); }
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| fn main() { let string1 = String::from("long string is long"); let result; { let string2 = String::from("xyz"); result = longest(string1.as_str(), string2.as_str()); } println!("The longest string is {result}"); }
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
|
深入理解生命周期
如果将 longest 函数的实现修改为总是返回第一个参数而不是最长的字符串 slice,就不需要为参数 y 指定一个生命周期
1 2 3
| fn longest<'a>(x: &'a str, y: &str) -> &'a str { x }
|
当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。如果返回的引用没有 指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值。然而它将会是一个悬垂引用,因为它将会在函数结束时离开作用域。这种情况即使指定生命周期编译也通不过,如下例子
1 2 3 4
| fn longest<'a>(x: &str, y: &str) -> &'a str { let result = String::from("really long string"); result.as_str() }
|
我们尝试从函数返回一个 result 的引用。无法指定生命周期参数来改变悬垂引用,而且 Rust 也不允许我们创建一个悬垂引用。在这种情况,最好的解决方案是返回一个有所有权的数据类型而不是一个引用,这样函数调用者就需要负责清理这个值了。
结构体中的生命周期
1 2 3
| struct ImportantExcerpt<'a> { part: &'a str, }
|
这个注解意味着 ImportantExcerpt 的实例不能比其 part 字段中的引用存在的更久
生命周期省略
函数或方法的参数的生命周期被称为 输入生命周期(input lifetimes),而返回值的生命周期被称为 输出生命周期(output lifetimes)
三条默认的生命周期规律
1 2 3
| 1、函数有一个引用参数的就有一个生命周期参数:fn foo<'a>(x: &'a i32),有两个引用参数的函数就有两个不同的生命周期参数,fn foo<'a, 'b>(x: &'a i32, y: &'b i32),依此类推 是如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:fn foo<'a>(x: &'a i32) -> &'a i32。 是如果方法有多个输入生命周期参数并且其中一个参数是 &self 或 &mut self,说明是个对象的方法 (method),那么所有输出生命周期参数被赋予 self 的生命周期
|
静态生命周期
‘static,其生命周期能够存活于整个程序期间
总结
在同一函数中指定泛型类型参数、trait bounds 和生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| fn main() { let string1 = String::from("abcd"); let string2 = "xyz";
let result = longest_with_an_announcement( string1.as_str(), string2, "Today is someone's birthday!", ); println!("The longest string is {result}"); }
use std::fmt::Display; fn longest_with_an_announcement<'a, T>( x: &'a str, y: &'a str, ann: T, ) -> &'a str where T: Display, { println!("Announcement! {ann}"); if x.len() > y.len() { x } else { y } }
|