1、生命周期
Rust的每个引用都有自己的生命周期。
生命周期:引用保持有效的作用域。
大多数情况:生命周期是隐式的、可被推断的
当引用的生命周期可能以不同的方式互相关联时:手动标注生命周期。
生命周期-避免悬垂引用( dangling reference)
生命周期的主要目标:避免悬垂引用( dangling reference)
fn main() {
{
let r;
{
let x = 5;
r = &x;
}
println!("r,{}",r);
}
}
上面这部分代码就会报错
它报错的原因:就是因为r所指向的x离开}后就已经离开作用域了
借用检查器
Rust编译器的借用检查器:比较作用域来判断所有的借用是否合法。
函数中的泛型生命周期
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
}
fn longest(x:&str,y:&str)->&str {
if x.len() > y.len() {
x
} else {
y
}
}
我们运行上面的代码会发现有报错
缺少生命周期的标注
然后我们这么改:
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
}
fn longest<'a>(x:&'a str,y:&'a str)->&'a str {
if x.len() > y.len() {
x
} else {
y
}
}
2、生命周期标注语法
生命周期的标注不会改变引用的生命周期长度
当指定了泛型生命周期参数,函数可以接收带有任何生命周期的引用
生命周期的标注:描述了多个引用的生命周期间的关系,但不影响生命周期
生命周期标注–语法
生命周期参数名:
以’开头
通常全小写且非常短―很多人使用‘a
生命周期标注的位置:
在引用的&符号后
使用空格将标注和引用类型分开
生命周期标注–例子
&i32 //一个引用
& 'a i32/带有显式生命周期的引用
&'a mut i32 //带有显式生命周期的可变引用
单个生命周期标注本身没有意义
函数签名中的生命周期标注
泛型生命周期参数声明在:函数名和参数列表之间的<>里
fn main() {
let string1 = String::from("abcd");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("{}",result)
}
fn longest<'a>(x:&'a str,y:&'a str)->&'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这一段代码就会报错,,那是因为string2的生命周期不够长
生命周期‘a的实际生命周期是:x和y两个生命周期中较小的那个
深入理解生命周期
指定生命周期参数的方式依赖于函数所做的事情
fn main() {
let string1 = String::from("abcd");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("{}",result)
}
fn longest<'a>(x:&'a str,y:&str)->&'a str {
x
}
从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配:
如果返回的引用没有指向任何参数,那么它只能引用函数内创建的值:
这就是悬垂引用:该值在函数结束时就走出了作用域
fn main() {
let string1 = String::from("abcd");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("{}",result)
}
fn longest<'a>(x:&'a str,y:&str)->&'a str {
let result = String::from("abc");
result.as_str()
}
这里就会报错,如果我们要改这一段错误,就直接返回它的值而不是它的引用,即把它的所有权移交给了函数的调用者,这段内存的清理工作也交给了函数的调用者。
Struct 定义中的生命周期标注
struct ImportantExcept<'a> {
part: &'a str,
}
fn main() {
let novel = String::from("Call me Ismael, Some years ago...");
let first_sentence = novel.split(".")
.next()
.expect("Could not found a '.'");
let i = ImportantExcept{
part: first_sentence
};
}
生命周期的省略我们知道:
每个引用都有生命周期
需要为使用生命周期的函数或struct指定生命周期参数
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
}
fn main() {
}
生命周期省略规则
在Rust引用分析中所编入的模式称为生命周期省略规则。
这些规则无需开发者来遵守
它们是一些特殊情况,由编译器来考虑
如果你的代码符合这些情况,那么就无需显式标注生命周期
生命周期省略规则不会提供完整的推断:
如果应用规则后,引用的生命周期仍然模糊不清→编译错误
解决办法:添加生命周期标注,表明引用间的相互关系
输入、输出生命周期
生命周期在:
函数/方法的参数:输入生命周期
函数/方法的返回值:输出生命周期
生命周期省略的三个规则
编译器使用3个规则在没有显式标注生命周期的情况下,来确定引用的生命周期
规则1应用于输入生命周期
规则2、3应用于输出生命周期
如果编译器应用完3个规则之后,仍然有无法确定生命周期的引用→报错
这些规则适用于fn定义和impl块
规则1:每个引用类型的参数都有自己的生命周期
规则2:如果只有1个输入生命周期参数,那么该生命周期被赋给所有的输出生命周期参数
规则3:如果有多个输入生命周期参数,但其中一个是&self或&mut self(是方法),那么self的生命周期会被赋给所有的输出生命周期参数
生命周期省略的三个规则-例子
假设我们是编译器:
fn first_word(s: &str) -> &str {
fn first_word<'a>(s: &'a str) -> &str {
fn first_word<'a>(s: &'a str) -> &'a str {
fn longest(x: &str, y: &str) -> &str {
fn longest<'a, 'b>(x: & 'a str, y: &'b str) -> &str { // 这里就会报错
方法定义中的生命周期标注
在struct上使用生命周期实现方法,语法和泛型参数的语法一样
在哪声明和使用生命周期参数,依赖于:
生命周期参数是否和字段、方法的参数或返回值有关
struct字段的生命周期名:
在impl后声明
在 struct名后使用
这些生命周期是struct类型的一部分
impl块内的方法签名中:
引用必须绑定于struct字段引用的生命周期,或者引用是独立的也可以
生命周期省略规则经常使得方法中的生命周期标注不是必须的
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl <'a> ImportantExcerpt<'a> {
fn level(&self)->i32 {
3
}
fn announce_and_return_part(&self,announcement:&str)->&str {
println!("Attention please;{}",announcement);
self.part
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split(".").next().expect("Could not found a '.'");
let i = ImportantExcerpt{
part: first_sentence,
};
}
静态生命周期
'static是一个特殊的生命周期:整个程序的持续时间。
例如:所有的字符串字面值都拥有‘static生命周期
let s: & 'static str = "l have a static lifetime.";
为引用指定‘static生命周期前要三思:
是否需要引用在程序整个生命周期内都存活。
泛型参数类型、Trait Bound、生命周期
use std::fmt::Display;
fn longest_with_an_annoucement<'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
}
}
fn main() {
}