走进Rust:引用的生命周期

Rust大约 3551 字

定义

借用检查器borrow checker会比较作用域来确保所有的引用都是有效的。

x的引用的生命周期是'b,即r = &x;后引用就被回收了,而r指向了x的引用,但在'b作用域后r指向的是一个被回收的地址,形成了类似于Java中的null,这在Rust中是不允许的。

{
    let r;                // ---------+-- 'a
                          //          |
    {                     //          |
        let x = 5;        // -+-- 'b  |
        r = &x;           //  |       |
    }                     // -+       |
                          //          |
    println!("r: {}", r); //          |
}                         // ---------+

会得到错误

error[E0597]: `x` does not live long enough
  --> src/main.rs:7:5
   |
6  |         r = &x;
   |              - borrow occurs here
7  |     }
   |     ^ `x` dropped here while still borrowed
...
10 | }
   | - borrowed value needs to live until here

语法

&i32        // 引用
&'a i32     // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用

生命周期省略规则

函数或方法的参数的生命周期被称为输入生命周期

返回值的生命周期被称为输出生命周期

第一条规则

有一个引用参数的函数有一个生命周期参数:

fn foo<'a>(x: &'a i32) {}

有两个引用参数的函数有两个不同的生命周期参数。

fn foo<'a, 'b>(x: &'a i32, y: &'b i32) {}

第二条规则

如果只有一个输入生命周期参数,那么输出生命周期参数就等于输入生命周期参数。

fn foo<'a>(x: &'a i32) -> &'a i32

第三条规则

如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为 &self 或 &mut self,那么 self 的生命周期被赋给所有输出生命周期参数。

fn level(&self) -> i32 {}

函数中的引用生命周期

函数中的名称以及入参和返回值都标注有生命周期参数'a

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

错误调用:此处的longest函数的返回值被指向了result,而longest函数的返回值在内部大括号作用域结束后已经被回收,导致在println时再次引用出现问题。(没有println这句代码编译能通过。)

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!("The longest string is {}", result);
}

// 错误信息

error[E0597]: `string2` does not live long enough
  --> src/main.rs:15:5
   |
14 |         result = longest(string1.as_str(), string2.as_str());
   |                                            ------- borrow occurs here
15 |     }
   |     ^ `string2` dropped here while still borrowed
16 |     println!("The longest string is {}", result);
17 | }
   | - borrowed value needs to live until here

正确调用:

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!("The longest string is {}", result);
    }
}

结构体中的引用生命周期

定义字段中包含引用生命周期的结构体。

struct ImportantExcerpt<'a> {
    part: &'a str,
}

使用该结构体。注意:ImportantExcerpt实例的作用域要小于等于part引用的作用域。

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.')
        .next()
        .expect("Could not find a '.'");
    let i = ImportantExcerpt { part: first_sentence };
}

方法中的引用生命周期

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
}
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

静态生命周期

'static,其生命周期能够存活于整个程序期间。

let s: &'static str = "I have a static lifetime.";

综合示例

结合泛型类型参数、trait bounds和生命周期

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
    }
}
阅读 155 · 发布于 2020-07-23

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

昵称:
随便看看换一批