惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
Y
Y Combinator Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
I
InfoQ
C
Check Point Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
Vercel News
Vercel News
Last Week in AI
Last Week in AI
GbyAI
GbyAI
P
Proofpoint News Feed
量子位
Stack Overflow Blog
Stack Overflow Blog
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
B
Blog
T
The Blog of Author Tim Ferriss
H
Help Net Security
云风的 BLOG
云风的 BLOG

博客园 - _朝晖

Claude Code创始人谈AI时代角色融合:未来团队需要这五种人 docker proxy setting kali linux上安装docker 高频交易策略算法简单示例 hot topics skill Polymarket 套利圣经:真正的差距在数学基础设施 一文读懂大模型交互模式:CoT、ReAct、ReWOO与Reflexion技术解析 Polymarket十大交易策略(附实例) 拆解Polymarket五大套利流派:普通玩家如何抓住百万美金机会? HFT策略算法简单示例 多智能体并行协作开发模式 Claude Code Agent Teams 完整上手攻略 Claude Code 启用 LSP 完全指南:2 分钟让 AI 编程速度提升 900 倍 Claude Code 免费从入门到精通 Claude Sonnet 4.6空降!Office性能干翻旗舰模型,软件股哀嚎一片 Claude Code从小白到大神的10000字终极指南 如何使用claude的plan模式? 2025年网络安全十大发展趋势发布 HLOB:限价订单簿中的信息持久性和结构 端口扫描工具横向对比测评 rust的枚举多态 Rust中的三种多态性——Enum和Trait(上) Rust OO:多态与继承 AFL漏洞挖掘技术漫谈(一):用AFL开始你的第一次Fuzzing
Rust生命周期,看这一篇就够了~
_朝晖 · 2024-07-19 · via 博客园 - _朝晖

https://blog.csdn.net/vince1998/article/details/138324413

生命周期为什么要提出、是什么、怎么用
导航
生命周期为什么要提出、是什么、怎么用
一、生命周期为什么要提出
二、生命周期是什么
三、生命周期怎么用
1、生命周期标注
(1)引用类型标注
(2)函数参数生命周期标注
(3)结构体字段中生命周期标注
(4)方法中生命周期声明
(5)静态生命周期
2、赋值时生命周期规则
3、生命周期消除
四、特别案例
一、生命周期为什么要提出
生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据

{
    let r;

    {
        let x = 5;
        r = &x;
    }//x已失效

    println!("r: {}", r);//r此时引用了一个无效数据,称r为悬垂引用,报错
}
1
2
3
4
5
6
7
8
9
10
二、生命周期是什么
通过生命周期的分析,确保所有权和借用的正确性,感觉可以说生命周期又是Rust本身的一种语言特性。。。。

三、生命周期怎么用
1、生命周期标注
生命周期标注通常是用在引用类型上的。

标注方式是‘+生命周期名称

(1)引用类型标注
&i32        // 一个引用
&'a i32     // 具有显式生命周期的引用,生命周期名称是a
&'a mut i32 // 具有显式生命周期的可变引用
1
2
3
(2)函数参数生命周期标注
fn useless<'a>(first: &'a i32, second: &'a i32) {}
1
函数名称后的<'a>只是生命周期名称声明,类似泛型也需要在函数名称后声明一样

上面的意思是,first参数和second参数的生命周期名称为‘a,这两个参数的实际生命周期是大于等于’a的

fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
    println!("x is {} and y is {}", x, y);
}
1
2
3
这段代码的意思是print_refs 有两个引用参数,它们的生命周期 'a 和 'b 至少得跟函数活得一样久

(3)结构体字段中生命周期标注
#[derive(Debug)]
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,
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
这段代码的意思是,结构体和结构体实例的生命周期为’a,结构体ImportantExcerpt 所引用的字符串 str 生命周期需要大于等于该结构体的生命周期,否则报错。

那你可能会问,字段引用怎么才会小于结构体的生命周期,那我们来看一个相关例子

#[derive(Debug)]
struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let i;
    {
        let novel = String::from("Call me Ishmael. Some years ago...");
        let first_sentence = novel.split('.').next().expect("Could not find a '.'");
        i = ImportantExcerpt {
            part: first_sentence,
        };
    }// String::from("Call me Ishmael. Some years ago...")已失效
    println!("{:?}",i);//i拥有的结构体中的字段指向失效的地址内容,也就是悬垂指针,报错
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
此时就会报错

error[E0597]: `novel` does not live long enough
  --> src/main.rs:10:30
   |
10 |         let first_sentence = novel.split('.').next().expect("Could not find a '.'");
   |                              ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
14 |     }
   |     - `novel` dropped here while still borrowed
15 |     println!("{:?}",i);
   |                     - borrow later used here

1
2
3
4
5
6
7
8
9
10
11
(4)方法中生命周期声明
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str
    where
        'a: 'b,//约束,b生命周期要小于a的生命周期
    {
        println!("Attention please: {}", announcement);
        self.part
    }
}

1
2
3
4
5
6
7
8
9
10
(5)静态生命周期
let s: &'static str = "我没啥优点,就是活得久,嘿嘿";
1
生命周期 'static 意味着能和程序活得一样久,例如字符串字面量和特征对象
实在遇到解决不了的生命周期标注问题,可以尝试 T:'static,有时候它会给你奇迹
2、赋值时生命周期规则
fn longest<'a:'b,'b>(x: &'a str, y: &'b str) -> &'b str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
1
2
3
4
5
6
7
在返回值有多个 不同生命周期 的值返回时,要求返回类型是 所有返回值中 生命周期最小的那个。
这里有一个生命周期赋值的基本原则

短命的才可以引用长命的,长命的引用短命的,当长命的想使用短命的,万一短命的已经挂了,那就发生悬挂引用现象,导致报错。

声明生命周期之间的长短关系,以上面的函数longest为例子,'a:'b表示为,在’a、'b两个生命周期中,'b的生命周期小于’a的生命周期。

那么在函数体内,无论返回x还是y,返回类型的生命周期至少都是小于或等于x和y的,满足借用安全,不会发生悬垂引用

3、生命周期消除
实际上,对于编译器来说,每一个引用类型都有一个生命周期,那么为什么我们在使用过程中,很多时候无需标注生命周期?例如

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}
1
2
3
4
5
6
7
8
9
10
11
该函数的参数和返回值都是引用类型,尽管我们没有显式的为其标注生命周期,编译依然可以通过。其实原因不复杂,编译器为了简化用户的使用,运用了生命周期消除大法。

减少了程序员的工作量

但是在生命周期消除不是万能的,需要注意的是

消除规则不是万能的,若编译器不能确定某件事是正确时,会直接判为不正确,那么你还是需要手动标注生命周期
函数或者方法中,输入参数的生命周期被称为 输入生命周期,返回值的生命周期被称为 输出生命周期
然后生命周期消除主要遵循一下三个规则

每一个引用参数都会获得独自的生命周期
例如一个引用参数的函数就有一个生命周期标注: fn foo<'a>(x: &'a i32),两个引用参数的有两个生命周期标注:fn
foo<'a, 'b>(x: &'a i32, y: &'b i32), 依此类推

若只有一个输入生命周期(函数参数中只有一个引用类型),那么该生命周期会被赋给所有的输出生命周期,也就是所有返回值的生命周期都等于该输入生命周期
例如函数 fn foo(x: &i32) -> &i32,x 参数的生命周期会被自动赋给返回值 &i32,因此该函数等同于 fn
foo<'a>(x: &'a i32) -> &'a i32

若存在多个输入生命周期,且其中一个是 &self 或 &mut self,则 &self 的生命周期被赋给所有的输出生命周期
拥有 &self 形式的参数,说明该函数是一个 方法,该规则让方法的使用便利度大幅提升。

四、特别案例
看一段代码

struct Interface<'a> {
    manager: &'a mut Manager<'a>
}

impl<'a> Interface<'a> {
    pub fn noop(self) {
        println!("interface consumed");
    }
}

struct Manager<'a> {
    text: &'a str
}

struct List<'a> {
    manager: Manager<'a>,
}

impl<'a> List<'a> {
    pub fn get_interface(&'a mut self) -> Interface {//Interface生命周期和List实例生命周期相同
        Interface {
            manager: &mut self.manager
        }
    }
}

fn main() {
    let mut list = List {
        manager: Manager {
            text: "hello"
        }
    };

    list.get_interface().noop();

    println!("Interface should be dropped here and the borrow released");

    // 下面的调用会失败,因为同时有不可变/可变借用
    // 但是Interface在之前调用完成后就应该被释放了
    use_list(&list);
}

fn use_list(list: &List) {
    println!("{}", list.manager.text);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
发生错误

error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable // `list`无法被借用,因为已经被可变借用
  --> src/main.rs:40:14
   |
34 |     list.get_interface().noop();
   |     ---- mutable borrow occurs here // 可变借用发生在这里
...
40 |     use_list(&list);
   |              ^^^^^
   |              |
   |              immutable borrow occurs here // 新的不可变借用发生在这
   |              mutable borrow later used here // 可变借用在这里结束

1
2
3
4
5
6
7
8
9
10
11
12
这是因为Interface生命周期和List实例生命周期相同

  pub fn get_interface(&'a mut self) -> Interface {//Interface生命周期和List实例生命周期相同
        Interface {
            manager: &mut self.manager
        }
    }
1
2
3
4
5
然后主函数后面

list.get_interface().noop();//interface还没有drop
println!("Interface should be dropped here and the borrow released");
use_list(&list);//这句之后interface才drop,但是已经同时出现可变引用和不可变引用
1
2
3
那我们可以这样改

struct Interface<'b, 'a: 'b> {
    manager: &'b mut Manager<'a>
}

impl<'b, 'a: 'b> Interface<'b, 'a> {
    pub fn noop(self) {
        println!("interface consumed");
    }
}

struct Manager<'a> {
    text: &'a str
}

struct List<'a> {
    manager: Manager<'a>,
}

impl<'a> List<'a> {
    pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a>
    where 'a: 'b {
        Interface {
            manager: &mut self.manager
        }
    }
}

fn main() {

    let mut list = List {
        manager: Manager {
            text: "hello"
        }
    };

    list.get_interface().noop();

    println!("Interface should be dropped here and the borrow released");

    // 下面的调用可以通过,因为Interface的生命周期不需要跟list一样长
    use_list(&list);
}

fn use_list(list: &List) {
    println!("{}", list.manager.text);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
作者给出修改后的代码
impl<'a> List<'a> {
    pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a>
    where 'a: 'b {
        Interface {
            manager: &mut self.manager
        }
    }
}
因为 'a: 'b,调用此方法的引用和interface生命周期的长度是一样的,当主函数的
list.get_interface().noop();
结束后
interface其实是可以死(可以比list的生命周期短)的,所以关于manager: &mut self.manager的可变引用也是可以死了的,所以不会后续的
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/vince1998/article/details/138324413