Rust进阶[part3]_生命周期

AI-摘要
sonia33 GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
Rust进阶[part3]_生命周期
SoniaChenRust进阶[part3]_生命周期
生命周期概述
在Rust中,生命周期是一种确保引用有效性的机制。Rust编译器通过生命周期注解来跟踪引用的作用域,防止出现悬空引用(dangling references)。
简单使用
下面是一个简单的生命周期注解示例:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(&string1, string2);
println!("The longest string is {}", result);
}
在这个例子中,'a
是一个生命周期参数,它表示 x
和 y
引用的字符串切片必须至少和 'a
一样长。函数返回的引用也必须和 'a
一样长。
类别
fn
函数可以有生命周期参数,用于指定函数参数和返回值的生命周期关系。如上面的 longest
函数示例。
struct
结构体也可以包含引用类型的字段,这时需要为这些引用添加生命周期注解。
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,
};
}
这里的 'a
表示 part
字段引用的字符串切片必须至少和 ImportantExcerpt
实例一样长。
enum
枚举同样可以包含引用类型的字段,需要添加生命周期注解。
enum MaybeString<'a> {
Some(&'a str),
None,
}
fn main() {
let s = String::from("hello");
let maybe_str = MaybeString::Some(&s);
}
生命周期消除
rust编译器自动推理,无需手动重复添加
- 每个引用参数都有自己的生命周期
- 如果只有一个输入引用参数,那么它的生命周期会被赋予所有输出引用
- 如果有多个输入生命周期参数,但是其中一个是
&self
或者&mut self
,那它的生命周期也会被赋予所有输出引用 ,比如结构体和枚举自己实现的方法中
示例
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
在这个函数中,虽然没有显式地添加生命周期注解,但编译器根据生命周期消除规则,自动为 s
和返回值添加了相同的生命周期。
特殊生命周期的标注
全局变量或者字符串字面量的生命周期在整个程序运行期间都有效,一般是使用 'static
来标注的生命周期。
示例
let s: &'static str = "I have a static lifetime.";
这里的 s
是一个字符串字面量,它的生命周期是 'static
,意味着它在整个程序运行期间都有效。
生命周期约束
生命周期注解可以用来约束多个引用之间的关系。
示例
fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
where
T: std::fmt::Display,
{
println!("Announcement! {}", ann);
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let ann = "Let's find the longest string!";
let result = longest_with_an_announcement(&string1, string2, ann);
println!("The longest string is {}", result);
}
在这个例子中,'a
约束了 x
和 y
引用的字符串切片的生命周期,同时也约束了返回值的生命周期。
生命周期子类型和协变
生命周期可以有子类型关系,较短的生命周期可以被视为较长生命周期的子类型。这在协变(covariance)中尤为重要。
示例
fn main() {
let r;
{
let x = 5;
r = &x;
} // x 在这里离开作用域,r 变成悬空引用
println!("r: {}", r);
}
在这个例子中,x
的生命周期比 r
短,x
的生命周期是 r
生命周期的子类型。当 x
离开作用域时,r
变成悬空引用,这段代码会编译错误。
练习
要求:修复一下原始代码的bug
原始代码
fn test_lifetime_multiple() {
fn insert_value<'a, 'b>(my_vec: &'a mut Vec<&'a i32>, value: &'b i32) {
my_vec.push(value)
}
let mut my_vec: Vec<&i32> = vec![];
let val1 = 1;
let val2 = 2;
let a = &mut my_vec;
insert_value(a, &val1);
println!("a is {:?} ", a);
let b = &mut my_vec;
insert_value(b, &val2);
println!("b is {:?}", b);
println!("{my_vec:?}");
}
修改后的代码
fn test_lifetime_multiple() {
fn insert_value<'a, 'b>(my_vec: &'a mut Vec<&'b i32>, value: &'b i32) {
my_vec.push(value)
}
let mut my_vec: Vec<&i32> = vec![];
let val1 = 1;
let val2 = 2;
let a = &mut my_vec;
insert_value(a, &val1);
println!("a is {:?} ", a);
let b = &mut my_vec;
insert_value(b, &val2);
println!("b is {:?}", b);
println!("{my_vec:?}");
}
解释
分离生命周期参数:
fn insert_value<'a, 'b>(my_vec: &'a mut Vec<&'b i32>, value: &'b i32)
'a
是my_vec
可变引用的生命周期'b
是存储在Vec
中的引用的生命周期
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果