3 生命周期省略
3 分钟阅读
上一章我们了解了为什么需要生命周期。编译器无法自动判断参数或返回值中的引用如何相互关联,因此我们需要告诉编译器这些引用是相关的。
话虽如此,你大概写过需要引用(很可能是 &str)的 Rust 函数,却从未写过生命周期。那时为什么不必标注?Rust 中有一些常见模式,能让编译器清楚推断生命周期应该是什么。让我们看几个例子:
示例 1:返回值中没有引用
| |
在这个函数中,a 和 b 的生命周期不必相互关联。假设只有一个线程,且代码是安全的,它们所引用的变量在函数执行期间不可能被 drop;函数返回后,它们想存活多久都可以。
示例 2:输入中只有一个引用
| |
值得注意的是,(通常 {{footnote: 对于 static 类型(如字符串字面量)这是可能的,我们将在后面讲到}} )若引用不是传入的,就不可能创建引用并把它传出函数。因为引用必须指向你拥有的东西,而你拥有的东西在函数结束时会被 drop。因此你拥有的东西无法被引用;唯一能返回引用的方式,就是有人把引用传给了你。
因此,若参数中只有一个引用,你能返回的引用也只有那一个——参数的生命周期必须与返回的引用相同。
怎么办
Rust 本可以把这些情况专门写成例外;但那样可能会有大量例外,规则也会变得令人困惑。
相反,Rust 项目确定了一套编译器遵循的推断生命周期的流程。
编译器首先把函数签名中的所有引用分为两类:「输入」和「输出」。「输入」引用在函数参数(即实参)中;「输出」引用在函数返回类型中。
本章要学的两条规则是:
- 每个被省略(即「省略标注」)的输入生命周期,都会填入各自独立的生命周期。
- 若所有输入引用上恰好只有一个生命周期,该生命周期会赋给每一个输出生命周期。
看看这些规则如何影响上面两个例子,以及上一章的一个例子:
示例 1:返回值中没有引用
我们有:
| |
有两个输入生命周期,分别对应 a 和 b 的类型。各自分配独立的生命周期:
| |
没有输出生命周期,到此结束。
这个例子现在正确了——a 和 b 的两个生命周期可以完全无关;输出是拥有所有权的值,根本不依赖任何生命周期。
示例 2:输入中只有一个引用
我们有:
| |
只有一个输入生命周期(a 的类型需要):
| |
只有一个输出生命周期;且所有输入生命周期共享同一生命周期('elided1);因此可以把所有输出生命周期都设为该生命周期:
| |
现在就说得通了:返回 &i32 的唯一可能方式,就是从参数得到它;可以看到输入与输出共享同一生命周期。
示例 3:省略标注的局限
再看上一章的这个例子。
| |
与示例 1 一样,有两个输入生命周期,因此给它们分配不同的生命周期:
| |
但与示例 1 不同,这里需要输出生命周期!根据第二条规则,只有在恰好有一个输入生命周期时,才能省略输出生命周期。因此 Rust 认为此处省略生命周期是错误——用户必须提供更多信息!
练习:应用这些规则
本练习中有四个函数缺少部分生命周期标注。你的任务是手动遵循生命周期省略规则,按编译器会做的方式为这些函数补上生命周期。
在 lifetimekata 的未来版本中,这些会自动检查。目前完成后,请对照参考答案核对。