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
    }
}

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

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

这我们将str1str2、返回值的生命周期都设置为'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);
相关推荐
zqx_712 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己28 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2341 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河1 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai2 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端