rust生命周期初级

本文仅作笔记不作教程,仅作学习笔记 有任何错误请指出,部分参考自 rust圣经

每个引用都有生命周期 存在的目的: 避免悬垂引用

A 引用 B, 则必须保证 B的生命周期是大于A 注意这句话 后面是一直围绕着这句话展开

一個函數要返回的東西 其生命周期要確定 , 如 在if-else 中返回的兩個東西生命周期不同 既是不確定的狀態要用<'a> 來標注

對於單個變量 沒有什麽作用,一般用於汎型作用于多個引用 来告诉编译器两个泛型之间生命周期的联系

1
2
3
4
5
6
7
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

而且此處標注并不會改變任何變量的生命周期 它的作用就是一個注解告訴borrow checker 拿到最小的生命周期

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
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);
}

對於上述代碼 result 它所拿到的生命周期為<'a>

<'a>string1string2交集當中最小的那個 string2

观察上面代码发现 result【拿到】的生命周期 ==等于== <‘a> 但是 显然 result这个变量的生命周期变量是大于 <`a> 的 所以 明显不符。

那么只有让 result 的生命周期 等于 <`a> 也就是二者之间较小的那个就可以编译通过

1
2
3
4
5
6
7
8
fn main() {
    let string1 = String::from("long string is long");
    
    {
        let string2 = String::from("xyz");
        let result  = longest(string1.as_str(), string2.as_str());
    }
}

简而言之 这个规则就保证了 你不能用长的生命周期 来引用一个短的生命周期,只能用生命周期短的来引用比你生命周期长的

生命周期的三个规则

  1. 每个引用都会有自己的生命周期

  2. 若函数参数只有一个引用类型,那么该生命周期会被赋予给所有输出的生命周期 也就是所有返回值的生命周期都等于该输入生命周期

    例如函数 fn foo(x: &i32) -> &i32x 参数的生命周期会被自动赋给返回值 &i32,因此该函数等同于 fn foo<'a>(x: &'a i32) -> &'a i32

  3. 若存在多个输入生命周期,且其中一个是 &self&mut self,则 &self 的生命周期 默认被赋给所有的输出生命周期

结构体的生命周期

每个引用都有自己的生命周期, 那么如果在结构体里面有一个引用 那么编译器并不知道结构体 和 结构体里面的引用谁存活更久

所以要为结构体标明标识符

 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 find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

而对于这个标识符<'a> 实际上 二者生命周期的交集,

假设part活得久,那么在创建的struct的时候是可以有效引用

而假设 struct 活得久,就相当于是 用一个长生命周期的东西来引用 一个短生命周期的东西 就造成了悬垂引用 也就是上面的举例。

所以如果在struct里面有引用 一定要保证里面的引用都是比struct活得久 否则编译不会过

如果还不理解 看本文的第一句话。

方法的生命周期

运用第一和第三规则展开 这里不多做解释

由于编译器不知道 第二个参数 announcement 的生命周期到底为多长 所以会加上<'b>

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
struct ImportantExcerpt<'a> {
    part: &'a str,
}


#![allow(unused)]
fn main() {
    impl<'a> ImportantExcerpt<'a> {
        fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'a str {
            println!("Attention please: {}", announcement);
            self.part
        }
    }
}

还是做一个分析 这里的生命周期是一个交集 , 既<'a> <'b> 最小的交集

那为什么返回值 为<‘a> 是能够保证有效的呢

因为使用这个方法是在初始化结构后后做的 意味着 你的生命周期是比结构体短, 而短的生命周期是可以使用长的生命周期的东西。

如果我们把返回值的生命周期改为 <'b> 就会报错 因为返回值为self.part 生命周期为<‘a> 编译器并不知道

<'a><'b>的关系

1
2
3
4
5
6
7
8
9
#![allow(unused)]
fn main() {
    impl<'a> ImportantExcerpt<'a> {
        fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str {
            println!("Attention please: {}", announcement);
            self.part
        }
    }
}

在这里显然要保证 <'b> 必须比 <'a> 活得短 否则就会悬垂引用 所以就要使用 类似泛型约束的 生命周期约束 来告诉编译器 二者之间的关系

1
2
3
4
5
6
7
8
fn main() {
impl<'a: 'b, 'b> ImportantExcerpt<'a> {
    fn announce_and_return_part(&'a self, announcement: &'b str) -> &'b str {
        println!("Attention please: {}", announcement);
        self.part
    }
}
}

这个约束语法<'a:'b> 是告诉编译器 b 必须比 a活得短

不难发现 返回的是 self.part 若不要出现悬垂引用 只能短的生命周期引用长的生命周期,所以 'b 是要比'a 活得短

self.part是结构体里的东西 ,方法返回的是 <'b> 相当于 用 <'b>生命周期的东西引用 <'a> , 意味着 <'b> 必须比<'a>活得短

使用where简化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#![allow(unused)]
fn main() {
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str
    where
        'a: 'b,
    {
        println!("Attention please: {}", announcement);
        self.part
    }
}
}

静态生命周期

标注'static 遇到错误 不要轻易的使用static魔改.

事实上,关于 'static, 有两种用法: &'staticT: 'static,详细内容请参见此处

Licensed under CC BY-NC-SA 4.0