Rust 入门学习笔记(五):生命周期
借用,函数生命周期,结构体生命周期
本文属于我的 Rust 学习笔记 系列。
Rust 入门学习笔记以实际例子为主,讲解部分不是从零开始的,所以不建议纯萌新观看,读者最好拥有任意一种面向对象语言的基础,然后自己多多少少看过 Rust 的基本语法,刷过一点 rustlings。
Rust 进阶学习笔记以及实战的来源则五花八门,将会标注在下一行⬇️。
借用和生命周期
借用
可以认为,借用和引用是一个东西的两种描述。
- 引用(Reference):更看重对象
- 引用是一种变量的别名,通过
&
符号来创建,不会获取所有权 - 引用既可以是不可变的
&T
,也可以是可变的mut &T
- 引用可以在不传递所有权的情况下访问数据,安全且低开销
- 引用是一种变量的别名,通过
- 借用(Borrowing):更看重行为
- 通过引用来借用数据,从而在一段时间内访问而不拥有数据
- 可变借用
&mut
允许修改数据,但在生命周期内只能同时存在一个可变借用 - 不可变借用
&
不允许修改数据
借用检查规则
Rust 存在一个借用检查器(Borrow Checker)。它有以下规则:
- 不可变引用规则:在任意时间,可以有多个不可变引用。
- 可变引用规则:在任意时间,只能有一个可变引用访问数据,以防止并发修改导致的数据竞争。
- 生命周期规则:引用的生命周期必须在被引用的数据有效的时间范围内,以防止悬垂引用(即:引用的数据已经被销毁但引用还存在)。
- 在任意时间,要么有一个可变引用,要么有一或多个不可变引用,二者不能同时存在。
生命周期
一般情况下 Borrow Checker 能够自行推断生命周期。如果不行,就需要在函数/结构体的签名中指定。
生命周期只需要在定义声明,调用的时候是不需要的。
代码块内部的生命周期结束不会销毁其外部变量的生命周期。
例子
// 函数生命周期会自动推导为`'0`,也可以手动定义,一般会用 a b out...
函数生命周期
任何引用都有生命周期。
- 大多数情况下,生命周期都是隐式推断的。
- 生命周期的主要目的就是防止悬垂引用。
- Rust 能够在编译时检查代码,确保引用的有效性,而不是在运行时出现悬垂引用的错误。
推断规则
- 每个作为引用的参数都会得到它自己的生命周期参数。
- 如果只有一个输入生命周期参数,该参数会被分配给所有返回值。
- 如果存在多个输入生命周期参数,其中一个是对
&self
或&mut self
的引用,self
的生命周期会被分配给所有输出生命周期参数(因为这种情况下函数是self
的一个方法,所以生命周期需要是一样的)。
例子
// 如果没有返回,是不需要标注生命周期的
// 这里有多个入参,且返回了一个字面量的引用,就需要标注生命周期
// s1 s2 默认具有不同的生命周期('0 '1),可以写成相同的
// 为每一个参数和返回值标注生命周期
// 这样的好处是灵活,且性能好一些
// &'static 生命周期是程序结束,一般仅用于测试场景
结构体生命周期
结构体中的引用必须标注生命周期。但结构体的方法(&self等)不需要标注生命周期。
应尽量避免自己标注生命周期。
例子
// 这里只是为了演示,真正代码开发时结构体应该避免使用字面量,而是直接用 String
// 有生命周期的结构体的 impl 后面需要标注生命周期,但方法上不用标注
约束
类型约束
前面已经见过,这种限制生命周期的方式就是类型约束。
'a: 'b
:假设有两个引用 &'a i32 和 &'b i32,它们的生命周期分别是 'a 和 'b,若 'a >= 'b,则可以定义 'a: 'b,表示 'a 至少要活得跟 'b 一样久。
// 指定 a 的生命周期比 b 长
// where 语句的类型约束
特质约束
就像泛型类型可以有约束一样,生命周期也可以有约束。
- T: 'a,所有引用在类型 T 必须超过生命周期 'a
- T: Trait + 'a: T 必须实现特质 Trait 并且所有引用在 T 必须超过生命周期 'a
use Debug; // 特质约束使用
;
// `Ref` 包含对泛型类型 `T` 的引用,该泛型类型具有
// 未知的生命周期 `'a`. `T` 是约定任何
// 引用在 `T` 必须大于 `'a` 。此外,在生命周期
// 里 `Ref` 不能超过 `'a`。
// 使用 `Debug` 特质打印的通用函数。
// 这里引用 `T` 使用 where `T` 实现
// `Debug` 和所有引用 `T` 都要比 `'a` 长
// 此外,`'a`必须要比函数声明周期长
/* 使用生命周期注释结构体,假设我们要求
1. `r` 和 `s` 必须是不同生命周期
2. `s` 的生命周期需要大于 'r'
*/
高阶生命周期
高阶特质约束(HRTB,Higher-ranked trait bounds)的语法是for<'a>
。它显式告诉编译器:变量必须能处理任何生命周期的类型,而不仅仅是某个特定的生命周期
// 对于任意生命周期 'a,F 都必须实现 Fn(&'a i32)
📝 系列导航
- 上一篇: Rust 入门学习笔记(四):基础错误处理
- 下一篇: Rust 入门学习笔记(六):泛型
- 合集列表