Rust 为什么需要生命周期注解
Tags: rust
Published: 2020-02-26 05:14:10

```rust
fn main() {
    let b;
    {
        let a = 10;
        b = &a;
    }       
    println!("{}", b);  
}
```
此时，a的作用域比b要小。rust的生命周期检查器发现a的生命周期比b要大，但是拥有一个生命周期比它小的对象，编译器就会拒绝编译。简而言之，rust的编译器能帮助我们确定某个引用是否有效(无效的话在编译时就会报错)，以此来避免悬垂引用等一系列问题。

然而，现实是，编译器有时候并不能确定某个引用是否有效。我们来看下面这个例子:

```rust
fn longest_str(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let str1 = String::from("123");
    let str_longer;
    {
        let str2 = String::from("12345");
        str_longer = longest_str(&str1, &str2);
    }
    
    println!("{}",str_longer);
}

```


这段代码是无法编译通过的，报错信息如下:

    error[E0106]: missing lifetime specifier
     --> src/main.rs:1:37
      |
    1 | fn longest_str(x: &str, y: &str) -> &str {
      |                                     ^ expected lifetime parameter
      |
      = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`

    error: aborting due to previous error

    For more information about this error, try `rustc --explain E0106`.
    error: Could not compile `lifetime_test`.

    To learn more, run the command again with --verbose.
    
    
    
表明在函数longest_str里需要生命周期注解，这是为什么呢？我们来分析一下代码。在上面这段代码里，str_longer拥有的引用是否有效呢？答案是不确定。我们只从函数内部来看是无法确认哪个参数的生命周期长或者是短.

需要注意的是，生命周期注解并不改变任何引用的生命周期的长短。也就是说，单个生命周期其实是没什么意义的，生命周期最大的作用就是告诉 Rust 多个引用的泛型生命周期参数如何相互联系的。加入有一个有一个生命周期为'a的参数x，一个生命周期也为'a的参数y，那么就说明x与y的生命周期是一样长的。这么一来就可以解决刚才longest_str函数的问题了。我们给函数标上生命周期注解:

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

fn main() {
    let str1 = String::from("123");
    let str_longer;
    {
        let str2 = String::from("12345");
        str_longer = longest_str(&str1, &str2);
    }
    
    println!("{}",str_longer);
}
```


虽然仍然无法编译通过，但是错误信息变成了:

    error[E0597]: `str2` does not live long enough
      --> src/main.rs:14:41
       |
    14 |         str_longer = longest_str(&str1, &str2);
       |                                         ^^^^^ borrowed value does not live long enough
    15 |     }
       |     - `str2` dropped here while still borrowed
    16 |
    17 |     println!("{}",str_longer);
       |                   ---------- borrow later used here
       
       
       

即错误信息变成了编译器检查出引用生命周期无效了，而不是longest_str这个函数的错误了，即str2和str1生命周期不一致。我们只需要修改main函数里参数的生命周期，使str1与str2生命周期保持一致即可。

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

fn main() {
    let str1 = String::from("12345");
    let str_longer;
    let str2 = String::from("123");
    str_longer = longest_str(&str1, &str2);
    
    println!("{}",str_longer);
}
```


那么问题来了，在上面这段代码里，函数longest_str里，x，y和函数返回值的生命周期都是一致的，那我们可不可以将它们标注成不同的呢？就像

```rust
fn longest_str<'a, 'b, 'c>(x: &'a str, y: &'b str) -> &'c str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
```

这并没有任何意义, 生命周期注解说到底也只是起一个标识作用.并不会改变程序的任何逻辑.它只是用来给Rust生命周期检查器核对的.

最后，总结一下，当且仅当输出值的生命周期为所有输入值的生命周期交集的子集时，生命周期合法。这个应该很好理解，也很好的解释了我们举的例子中生命周期注解的用法。rust也正是通过这种方式解决了"共享"这一问题，从某种程度上保证了内存的安全。



### 总结

生命周期注解只是给编译器检查使用的。
注解本身无法改变任何生命周期。
但是标出后能尽量让程序和你预期的一样工作。