Rust学习_5.生命周期

生命周期

  • Rust 的每个引用都有自己的生命周期;

  • 生命周期:引用保持有效的作用域;

  • 大多数情况:生命周期是隐式的、可被推断的;

  • 当引用的生命周期可能以不同的方式互相关联时:手动标注生命周期;

  • 目标:避免悬垂引用(dangling reference)。

一、生命周期的标注语法

  • 生命周期的标注不会改变引用的生命周期长度;

  • 当指定了泛型生命周期参数,函数可以接收带有任何生命周期的引用;

  • 生命周期的标注:描述了多个引用的生命周期间的关系,但不影响生命周期。

    1
    2
    3
    &i32 // 一个引用
    &'a i32 // 带有显示生命周期的引用
    &'a mut i32 // 带有显示生命周期的可变引用

二、函数签名中的生命周期标注

  • 泛型生命周期参数声明在:函数名和参数列表之间的<>里;
  • 生命周期 'a 的实际生命周期是:x和y两个生命周期中较小的那个。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let string1 = String::from("abc");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str()); // error:borrowed value does not live long enough
}
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
}
}

image-20211224000539875

三、深入理解生命周期

  • 指定生命周期参数的方式依赖于函数所做的事情
  • 从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配;
  • 如果返回的引用没有指向任何参数,那么它只能引用函数内创建的值:
    • 这就是悬垂引用:该值在函数结束时就走出了作用域;
    • 此时一般不返回引用,就直接返回值了。

四、Struct定义中的生命周期标注

  • Struct 中若定义引用类型则需要在每个引用上添加生命周期标注

  • Struct 中属性的生命周期一定要比Struct类型生命周期长,否则就要出错了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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 found a '.'");
    let i = ImportantExcerpt{
    part:first_sentence
    };
    }

五、生命周期的省略

  • 生命周期省略规则

1. 输入、输出生命周期

  • 输入生命周期:函数/方法的参数
  • 输出生命周期:函数/方法的返回值

2. 生命周期省略的三个规则

编译器使用三个规则在没有显示标注生命周期的情况下,来确定引用的生命周期

  • 规则1应用于输入生命周期;
  • 规则2、3应用于输出生命周期;
  • 如果编译器应用完3个规则之后,仍然有无法确定生命周期的引用则报错;
  • 这些规则适用于fn定义的和impl块。
  1. 规则1:每个引用类型的参数都有自己的生命周期;
  2. 规则2:如果只有一个输入生命周期参数,那么该生命周期被赋给所有的输出生命周期参数;
  3. 规则3:如果有多个输入生命周期参数,但其中一个是&self&mut self(是方法,不是函数),那么self的生命周期会被赋给所有的输出生命周期参数。

六、方法定义中的生命周期标注:

  • Struct上使用生命周期实现方法,语法和泛型参数的语法一样

  • 在哪儿声明和使用生命周期参数,依赖于:

    • 生命周期参数是否和字段、方法的参数或返回值有关;
  • Struct字段的生命周期名:

    • 在impl后声明
    • 在struct名后使用
    • 这些生命周期是struct类型的一部分
  • impl 块内的方法签名中:

    • 引用必须绑定于 struct 字段引用的生命周期,或者引用是独立的也可以;
    • 生命周期省略规则经常使得方法中的生命周期标注不是必须的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
}
}

七、静态生命周期

’static 是一个特殊的生命周期:整个程序的持续时间。

  • 例如:所有的字符串字面量值都拥有'static生命周期;

八、泛型参数类型、Trait Bount、生命周期

1
2
3
4
5
6
7
8
9
10
11
12
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
}
}