Rust第十节(下) - 生命周期

10.3 生命周期

Rust中的生命周期主要是用来解决悬垂引用的问题。首先我们来看看Rust中的悬垂引用问题怎么产生的。

10.3.1 悬垂引用

以下这个例子,我们在模块外声明了一个变量r,然后在模块给他赋值w的引用,但是模块里面的内容执行完就会销毁掉里面的内容,造成r变量指向一个空指针 ,形成了悬垂引用,如下:

rust 复制代码
 let r;                             //----------'a
{                                   //        |
    let w = String::from("stephen");//-----'b |
    r = &w;                         //  |     |
}                                   //---     | 
println!("{}", r);                  //--------+
// 值比变量的声明的范围小,造成悬垂引用

通过上面例子来看,当我们声明的变量比值的生命周期更长时,这个时候就会触发悬垂引用。那么Rust是怎么检测出来的呢?

10.3.2 生命周期标注规则

Rust有一个专门处理引用数据的检查器,叫借用检查器。它负责Rust整个生命周期的检查。

接下来我们就来了解一下生命周期的标注规则,我们一般使用' + 小写字母来表示一个生命周期,例如: 'a, 'b等等。

当我们需要配合着&使用,我们就需要在后面再加一个空格,例如:

rust 复制代码
&str // 引用
&'a str // 带有生命周期标注的引用
&'a mut str  // 带有生命周期标注的可变引用

10.3.3 省略规则

在实践之前,我们再来了解一下,借用检查机器内置的一些省略规则: 输入生命周期 类似 函数传参 输出生命周期 类似 函数返回值

第一条: 每个传入的参数都一个生命周期,传入一个参数有一个输入生命周期,传入两个参数则有两个生命周期

第二条: 如果只有一个参数,那么输出生命周期和输入生命周期相同

第三条: 在结构体 和枚举 的方法中,当拥有多个输入生命周期参数,而其中一个是&self或&mut self时,self的生命周期会被赋予给所有的输出生命周期参数。

10.3.4 函数添加生命周期标注

接下来我们尝试写一个比较两段字符串长度的函数,例如:

rust 复制代码
 fn longest(str1: &str, str2: &str) -> &str {
    if str1.len() > str2.len() {
        str1
    } else {
        str2
    }
}

但是因为这里传入的str1和str2的引用,我们并不知道他们的生命周期、什么时候会销毁,所以这里返回值,很有可能是一个悬垂引用,所以会编译失败。 我们来给他添加生命周期,保证引用的值比函数后面销毁,例如:

rust 复制代码
fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str {
    if str1.len() > str2.len() {
        str1
    } else {
        str2
    }
}

这我们将str1和str2、返回值的生命周期都设置为'a,然后就通过借用检查器的编译了。

10.3.5 结构体添加生命周期标注

我们在之前的结构体章节里面定义一个了一个User结构体,如下:

rust 复制代码
struct User {
    name: String,
}

当时我们没有使用&str,因为我们还没有设计到生命周期的概念,现在我们使用&str来代替这个name字段,例如:

rust 复制代码
struct User<'a> {
    name: &'a str,
}

10.3.7 静态生命周期

我们还可以在Rust中定义一个特别的生命周期'static,它只作用于字符串字面量,并且它作用于小程序从周期开始到结束,例如:

rust 复制代码
let str1: &'static str = "stephen";
println!("{}", str1);

注意 我们要慎用这个静态类型,它不仅会增加存储的时间周期、增加内存的消耗,作用于全局还会绕过借用检查器的规则。

10.3.8 泛型,特征,生命周期结合使用

最后,让我们来写一个泛型,特征,生命周期结合的函数结束第10节的学习。我们来改造改造之前的longest函数,如下:

rust 复制代码
fn longest<'a, T>(str1: &'a str, str2: &'a str, str3: T) -> &'a str
where
    T: Display,
{
    println!("extra={}", str3);
    if str1.len() > str2.len() {
        str1
    } else {
        str2
    }
}
let s1 = String::from("stephen");
let s2 = String::from("james");
let long = longest(&s1, &s2, 2);
println!("{}", long);
相关推荐
FungLeo6 小时前
成为全栈·React 管理后台篇·总复盘:从“接口能调通”到“后台值得使用”
前端·react.js·前端框架·项目复盘·成为全栈
Csvn7 小时前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_8 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
Flynt8 小时前
Linear 用 1000 个 PR 换掉 styled-components,我写了 200 个按钮,把这笔账复现了一遍
前端·css·preact
JavaGuide9 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel9 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
莪_幻尘10 小时前
Skill 体检:30 个 Skill 全凭感觉?体检器先自曝了 8 个“假 0 分
前端·人工智能·llm
风骏时光牛马10 小时前
AI模型综合能力评测:性能、指令遵循与多场景实测对比
前端
Frag0ut10 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
hiahiahia12311 小时前
实现完整 Tool Dispatcher
开发语言·前端