每个引用值都有生命周期

概念

生命周期: 引用保持有效的一个作用域

  • Rust的每个引用都有自己的生命周期
  • 生命周期是隐式的、可被推断的
  • 当引用的生命周期可能以不同的方式关联时:手动标注生命周期

    生命周期存在的意义就是避免悬垂引用

生命周期标注

首先明确一点:
生命周期标注并不会改变引用值的实际上的生命周期,也不会延长该生命周期,只是标注出一种关系。

  1. // 生命周期在函数签名中的标注
  2. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  3. if x.len() > y.len() {
  4. x
  5. } else {
  6. y
  7. }
  8. }

这里’a表示的意思其实就是x和y生命周期的交集,其实就是生命周期比较短的那个。

为了验证上面这句话,我们再来看一个例子

fn main() {
    let s1 = String::from("hello");
    let result;
    {
        let s2 = String::from("wold");
        result = longest(s1.as_str(), s2.as_str());
    }
    print!("{}", result);
}


fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

// error[E0597]: `s2` does not live long enough
//  --> src/main.rs:6:39
//   |
// 6 |         result = longest(s1.as_str(), s2.as_str());
//   |                                       ^^ borrowed value does not live long enough
// 7 |     }
//   |     - `s2` dropped here while still borrowed
// 8 |     print!("{}", result);
//   |                  ------ borrow later used here


// error: aborting due to previous error

我们来分析一下:
因为'a的生命周期是s1s2中较短的那个,那么很明显这里的 'a = s2's life time;

这样的话就可以理解报错的内容了。因为生命周期'a在花括号结束的时候就已经GG了,所以我们println打印的时候其实这个生命周期已经没了

这里再补充一个冷知识,如果let s2=”wold” 这样直接声明为字符切片的话,就不会报错。 因为字符切片的类型的是没有所有权的,字符切片直接存储与二进制文件中

深入理解生命周期

  • 指定生命周期参数的方式依赖于函数所做的事情

我们这里还是采用上面的例子,上面的例子因为有可能返回x也有可能返回y是不确定的。所以需要指定两个参数的生命周期

但是我们这次明确的返回x,所以这时生命周期y就没必要指定了

fn longest<'a>(x: &'a str, y: &str) -> &'a str {   
    x
}
  • 从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配

    • 如果返回的引用没有指向任何参数,那么它只能引用函数内创建的值,这就造成了悬垂引用(该值在函数结束时就走出了作用域)

      生命周期的省略

  • 在Rust引用分析中所编入的模式成为生命周期省略规则(就是一些通用可预测的模式)

    • 这些规则无需开发者遵守
    • 他们是一些特殊情况,由编译器来考虑
    • 如果代码符合这些情况,就无需显示的标注生命周期

生命周期省略的三个规则

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

静态生命周期

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

例如:
所有的字符串字面值都拥有static 生命周期,字符串字面值是直接被**存储在二进制文件中**的,所以他总是可用的let s:&static str = "hello"