【Rust自学】10.8. 生命周期 Pt.4:方法定义中的生命周期标注与静态生命周期

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

10.8.1. 方法定义中的生命周期标注

还记得在上一篇文章 10.7. 生命周期 Pt.3 中所提到的省略生命周期的三条规则吗:

规则1: 每个引用类型的参数都有自己的生命周期。 单参数的函数就有1个生命周期,双参数的函数就有两个,以此类推。

规则2: 如果只有1个输入生命周期参数,那么该生命周期被赋给所有的输出生命周期参数。 就是单参数的生命周期只有1个,这个生命周期就是这个函数所有可能返回值的生命周期。

规则3: 如果有多个输入生命周期参数,但其中一个是&self&mut self(也就是说是这个函数是方法),那么self的生命周期会被赋给所有输出的生命周期参数。

在上一篇文章的代码例中我们应用了规则1和2,但是规则3没有,因为规则3只适用于方法。所以这里就来讲一下规则3,也就是方法定义中的生命周期标注。

方法需要一个结构体,而在结构体上使用生命周期实现方法,它的语法和泛型参数的语法一样(详见文章 10.7. 生命周期 Pt.3)。

在哪里声明和使用生命周期参数,取决于生命周期参数是否和字段、方法的参数返回值有关。

结构体字段的生命周期名总是声明在impl关键字后面,然后在结构体名的后面进行使用。因为这些生命周期是结构体类型的一部分。

而在impl块内的方法签名中,引用 必须绑定于struct字段引用的生命周期,或者引用是独立的也可以。此外,生命周期省略规则经常使得方法中的生命周期标注不是必须的。

多说无益,看个例子:

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

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().unwrap();
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

首先定义了ImportantExcerpt这个结构体,然后为它定义了level这个方法。level这个方法的参数只有&self,返回值是i32类型,所以这个返回值没有引用任何东西。

上文所说的"结构体字段的生命周期名总是声明在impl关键字后面,然后在结构体名的后面进行使用 "指的就是第4行impl块后写了<'a>,在结构体名ImportantExcerpt后也写了<'a>

要注意的是第四行的两个<'a>一个都不能省略,但是level这个函数由于应用了省略生命周期标注的规则1和2,所以&self不需要加上生命周期标注。

然后再添加一个方法:

rust 复制代码
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {announcement}");
        self.part
    }
}

这个方法根据第1条省略规会为&selfannouncement两个参数各添加上一个生命周期:

rust 复制代码
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'a, 'b>(&'a self, announcement: &'b str) -> &str {
        println!("Attention please: {announcement}");
        self.part
    }
}

根据第3条省略规则,返回值会被赋予&self相同的生命周期:

rust 复制代码
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'a, 'b>(&'a self, announcement: &'b str) -> &'a str {
        println!("Attention please: {announcement}");
        self.part
    }
}

至此所有的生命周期都被推断出来了,所以编译器能通过编译。

10.8.2. 静态生命周期

Rust里有'static这个特殊的生命周期,它表示整个程序的持续时间,或者叫整个程序的执行期。

比如说,所有的字符串字面值都拥有'static生命周期,比如说:

rust 复制代码
let s = &'static str = "I have a static lifetime.";

这就是一个字符串字面值,所以可以用'static标注。

字符串字面值都拥有'static生命周期的原因是字符串字面值会被直接存储在二进制文件内,在运行时会放在静态内存中,所以它总是可用的。

在为普通的引用指定'static(编译器报错时经常会建议你这么做)前一定要三思:你倒是否需要这个引用在程序的整个生命周期内都存活 。因为编译器报错的原因大概率是因为悬空引用或是可用生命周期不匹配。这个时候应该尝试去解决这些问题而不是指定一个'static生命周期了事。

10.8.3. 泛型类型参数、trait bound、生命周期

最后看一个例子,它同时使用了泛型类型参数、trait bound和生命周期:

rust 复制代码
use std::fmt::Display;

fn longest_with_an_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: Display,
{
    println!("Announcement! {ann}");
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

这个函数的作用是返回xy这两个字符串切片中比较长的那一个,但此时它又多了一个参数ann,代表announcement,它的类型是泛型类型T,而根据where里的约束,T这个类型可以被替换为任何实现了Display这个trait的类型

相关推荐
lsx2024067 分钟前
Django 视图详解
开发语言
h***066514 分钟前
【JSqlParser】Java使用JSqlParser解析SQL语句总结
java·开发语言·sql
代码or搬砖24 分钟前
Java Lambda 表达式全面详解
java·开发语言·python
这周也會开心34 分钟前
JDK1.8新增语法
java·开发语言
随风飘的云34 分钟前
es搜索引擎的持久化机制原理
后端
心随雨下36 分钟前
TypeScript泛型开发常见错误解析
java·开发语言·typescript
Bigger40 分钟前
Tauri(十九)——实现 macOS 划词监控的完整实践
前端·rust·app
Se7en258141 分钟前
基于 MateChat 构建 AI 编程智能助手的落地实践
后端
郝学胜-神的一滴1 小时前
现代OpenGL窗口管理:GLFW从入门到实战
开发语言·c++·程序人生·图形渲染·个人开发
n***F8751 小时前
Skywalking介绍,Skywalking 9.4 安装,SpringBoot集成Skywalking
spring boot·后端·skywalking