Please enable Javascript to view the contents

[Rust 基础知识]初探生命周期标记

 ·  ☕ 2 分钟

前言

    生命周期标记主要用来处理生命周期跨函数时的情况。

标记形式

生命周期标记的一般形式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
&i32        // a reference
&'a i32     // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str 
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)

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

fn longest_with_an_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str


生命周期参数都有撇号(')作为前缀。如果标记的是引用,放在 & 后面。这个标记只起到修饰作用,不会影响参数的生命周期,可以理解为给编译器识别用的。

生命周期标记的一大用途就是显式声明包含关系。例如:

1
'a:'b

表示 ‘a 的生命周期一定大于等于 ‘b 的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
extern crate hello_rust;

fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32
where
    'a: 'b,
{
    println!("x={} y={}", x, y);
    x
}

fn main() {
    let num1: i32 = 1;
    let mut num1_ref = &num1;
    {
        let num2: i32 = 2;
        let mut num2_ref = &num2;
        num1_ref = foo(num1_ref, num2_ref);
    }

    println!("{:?}", num1_ref);
}

在上例中,如果把 foo 的返回值的生命周期标记改为 ‘b ,程序将会报错,因为函数标记的返回值生命周期和 ‘b 相同,而 num1_ref 超出了这个范畴。

再看一个结构体的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#[derive(Debug)]
struct Type<'a> {
    index: &'a i32,
}

fn main() {
    let i = 0;
    let mut t = Type { index: &i };

    {
        let i2 = 1;
        t.index = &i2;
    }

    println!("{:?}", t);
}

结构体通过标记 ‘a 要求作为 Type 的字段 &i32 来说,&i32 至少要和 Type 的生命周期一样长。而本例中 i2 明显生命周期短于 t,所以程序就会报错。

通过上面两个例子相信对于生命周期标记的职责已经有了一定了解。传通过统的代码语言有时很难表述出这种关系,只能留给程序员自己维护,在复杂的软件中这种生命周期问题一旦遇到往往很难排查。Rust 并没有增加问题,只是增加了一种解决问题的方式。

特殊的 static

    生命周期标记中有个特殊值 ‘static,它表示程序从开始到结束的整个生命周期。所以对于任意生命周期 ‘a 永远有 ‘static : ‘a。

生命周期标记的省略

    生命周期标记很重要,但是如果所有需要的地方我们都要手动标记的话怕不是要累死人了。所以生命周期标记可以省略:

1
2
fn first_word<'a>(s: &'a str) -> &'a str {
fn first_word(s: &str) -> &str

省略的生命周期标记不会有复杂的自动推导过程,除了上例外只有三条简单的规则:

第一条:对于输入参数一人分配一个生命周期标记。

1
2
fn foo<'a>(x: &'a i32) // fn foo(x:i32)
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)// fn foo(x:i32, y:i32)

第二条:如果只有一个输入参数指定了生命周期标记,那么将它应用于所有返回参数。
第三条:如果多个输入参数制定了生命周期标记,但是存在 &self 或者 &mut self,所有返回参数应用 self 的标记。

其他情况需要手动标记。

分享

saberuster
作者
saberuster
一个喜欢编程和折腾的追风少年