Rust需要在编译时确保所有引用都是有效的。生命周期就是用来追踪引用的有效范围的。
核心问题: 编译器如何知道引用是否指向有效的数据?
{ let r; // ---------+-- 'a // | { // | let x = 5; // -+-- 'b | r = &x; // | | } // -+ | // | println!("r: {}", r); // | } // ---------+
这段代码编译失败,因为r的生命周期'a比x的生命周期'b长。
&i32 // 引用 &'a i32 // 带有显式生命周期的引用 &'a mut i32 // 带有显式生命周期的可变引用
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已失效 }
如果结构体包含引用,必须标注生命周期:
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中的引用活得更长。
fn foo<'a>(x: &'a i32) // 一个参数 fn foo<'a, 'b>(x: &'a i32, y: &'b i32) // 两个参数
fn foo<'a>(x: &'a i32) -> &'a i32 // 省略写法:fn foo(x: &i32) -> &i32
impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { // 省略写法 3 } }
'static表示整个程序运行期间都有效。
let s: &'static str = "我有静态生命周期";
注意:
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 } }
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() }
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); }
答案: 使用生命周期省略规则即可,不需要显式标注。
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的生命周期:
生命周期是Rust内存安全保证的重要组成部分,虽然起初难以理解,但掌握后能写出更安全的代码。