rust:第十四章生命周期
目录
第十四章 生命周期
14.1 为什么需要生命周期
Rust需要在编译时确保所有引用都是有效的。生命周期就是用来追踪引用的有效范围的。
核心问题: 编译器如何知道引用是否指向有效的数据?
{ let r; // ---------+-- 'a // | { // | let x = 5; // -+-- 'b | r = &x; // | | } // -+ | // | println!("r: {}", r); // | } // ---------+
这段代码编译失败,因为r的生命周期'a比x的生命周期'b长。
14.2 生命周期标注语法
标注规则
- 以'开头,通常小写且简短,如'a、'b
- 放在&后面,如&'a i32
- 只是描述引用之间的关系,不改变实际生命周期
示例
&i32 // 引用 &'a i32 // 带有显式生命周期的引用 &'a mut i32 // 带有显式生命周期的可变引用
14.3 函数中的生命周期
生命周期省略规则
Rust有生命周期省略规则,在简单情况下自动推断:
1. 每个引用参数都有自己的生命周期 2. 如果只有一个输入生命周期,它赋给所有输出生命周期 3. 如果有多个输入生命周期,但一个是&self或&mut self,self的生命周期赋给输出
显式标注示例
// 编译错误:编译器无法推断返回值的生命周期 fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } }
正确写法:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
含义: 返回的引用与x和y中生命周期较短的那个相同。
使用示例
fn main() { let string1 = String::from("long string is long"); { let string2 = String::from("xyz"); let result = longest(string1.as_str(), string2.as_str()); println!("最长的字符串是{}", result); } }
错误使用示例
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!("最长的字符串是{}", result); // 错误!string2已失效 }
14.4 结构体中的生命周期
含引用的结构体
如果结构体包含引用,必须标注生命周期:
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("找不到."); let i = ImportantExcerpt { part: first_sentence, }; println!("{}", i.part); }
含义: ImportantExcerpt的实例不能比part中的引用活得更长。
14.5 生命周期的省略
规则1:每个引用参数有自己的生命周期
fn foo<'a>(x: &'a i32) // 一个参数 fn foo<'a, 'b>(x: &'a i32, y: &'b i32) // 两个参数
规则2:单个输入生命周期赋给输出
fn foo<'a>(x: &'a i32) -> &'a i32 // 省略写法:fn foo(x: &i32) -> &i32
规则3:&self的生命周期赋给输出
impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { // 省略写法 3 } }
14.6 静态生命周期
'static表示整个程序运行期间都有效。
let s: &'static str = "我有静态生命周期";
注意:
- 字符串字面量有'static生命周期
- 不要盲目使用'static来解决生命周期错误
- 通常意味着设计问题
14.7 泛型类型参数、Trait Bound和生命周期
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!("公告!{}", ann); if x.len() > y.len() { x } else { y } }
14.8 生命周期实践
返回引用时的生命周期
struct Parser<'a> { content: &'a str, } impl<'a> Parser<'a> { fn new(content: &'a str) -> Self { Parser { content } } fn first_word(&self) -> &'a str { self.content.split_whitespace().next().unwrap_or("") } }
多个生命周期
fn split<'a, 'b>(input: &'a str, delimiter: &'b str) -> Vec<&'a str> { input.split(delimiter).collect() }
练习题
练习题14.1:修复生命周期错误
fn get_first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") } fn main() { let text = String::from("hello world"); let word = get_first_word(&text); println!("{}", word); }
答案: 使用生命周期省略规则即可,不需要显式标注。
练习题14.2:实现包含引用的结构体
struct Config<'a> { filename: &'a str, content: &'a str, } impl<'a> Config<'a> { fn new(filename: &'a str, content: &'a str) -> Self { Config { filename, content } } fn display(&self) { println!("文件:{}", self.filename); println!("内容:{}", self.content); } } fn main() { let filename = "config.txt"; let content = "key=value"; let config = Config::new(filename, content); config.display(); }
本章小结
本章学习了Rust的生命周期:
- 生命周期的目的:确保引用总是有效
- 标注语法:'a、&'a T
- 函数生命周期:返回值与输入参数关联
- 结构体生命周期:含引用的结构体需要标注
- 省略规则:简化常见情况的标注
- 'static:整个程序运行期间有效
生命周期是Rust内存安全保证的重要组成部分,虽然起初难以理解,但掌握后能写出更安全的代码。
rust/第十四章生命周期.txt · 最后更改: 由 127.0.0.1
