每日一 R「05」生命周期
01-生命周期
Rust 中的值或其引用,根据其作用域的大小,可以划分为两类:
静态生命周期,即贯穿整个进程的生命周期。具有这类生命周期的值一般包括全局变量、静态变量、字符串字面量、使用 Box::leak 从堆中泄漏出去的值。静态生命周期一般通过 ‘static 或 &’static 表示,这种写法在 Rust 中称为生命周期标注。
动态生命周期,如果值或其引用是在某个作用域(例如函数作用域)中定义的,那么则称其具有动态生命周期。动态生命周期的标注一般表示为 ‘a 或 &’b,a 或 b 或者其他的什么符号都不重要。
课程中有一幅图总结了 Rust 中的生命周期与栈、堆上值的关系:
分配在栈上的值的生命周期与栈帧的生命周期绑定在一起;
分配在堆上的值,通过所有权、栈帧中的胖指针与栈帧的生命周期绑定在一起;
堆上通过 Box::leak 显式泄漏出去的值、全局变量、静态变量、字符串字面量、代码等内容与进程生命周期一致;
引用的生命周期是 Rust 中非常重要的一块内容,编译器也正是通过引用的生命周期检查来解决悬垂指针问题的。接下来我们来看一下编译器是如何识别引用的生命周期的,以及如何避免悬垂指针的。
02-悬垂指针和生命周期标注
悬垂指针本质是引用了已经释放的值,或者换个角度讲就是试图引用比自身生命周期短的值,思考如下的示例:
变量的 r 的生命周期 ‘a 从声明开始,到 println! 结束。变量 x 的生命周期 ‘b 从变量声明开始,到第一个 } 结束。r = &x; 尝试将 r 指向 x。当 } 后,x 因为离开作用域而被释放,那 r 对 x 的引用也将不再合法。所以 r 就变成了一个悬垂指针。这在程序中是非常危险的。如果我们继续尝试读取 r,则可能会导致程序不可预测的行为。
为了保证 Rust 的所有权和借用的正确性,Rust 使用了一个借用检查器(Borrow checker),来检查我们程序的借用正确性。在编译阶段,Rust 编译器会比较变量的生命周期。如果检查不通过,则认为我们编写的程序存在风险。
大多数情况下,编译器可以通过上下文推断出变量的生命周期。但在某些情况下,编译器并不能推断出来,这时候就需要认为地对生命周期进行标注。一个常用的例子就是比较字符串切片长度的例子:
在编译时,编译器并无法推断出入参 x、y 和返回值之间的生命周期关系,所以会抛异常。根据我们的方法实现,返回值要么是 x,要么是 y,这取决于哪个的长度更长。这也就意味着,返回值的生命周期至少要跟 x 和 y 中较短的那个声明周期一致。我们把入参和返回值的生命周期都标注为 ‘a 就意味着,’a 是他们三个至少要满足的生命周期。
生命周期标注并不会改变任何引用的实际作用域。在通过函数签名指定生命周期参数时,我们并没有改变传入引用或者返回引用的真实生命周期,而是告诉编译器当不满足此约束条件时,就拒绝编译通过。
再考虑如下代码:
采用之前的标注,上述代码运行会报错。我们来分析下报错原因:
先来看下 longest 函数的两个入参的生命周期,假设 string1 的生命周期为 ‘a,string2 的生命周期为 ‘b,明显 ‘a 长于(overlive) ‘b。
根据前面的推断,longest 函数返回值的生命周期至少跟两个入参中较短的那个一样长,所以返回值的生命周期应该至少跟 ‘b 一样长
result 生命周期 ‘c,从定义开始,到 println! 结束,这个显然比 ‘b 要长。
当 result = longest(); 时,属于前面介绍的悬垂指针情况,将较长生命周期的引用指向较短生命周期的值,所以报错。
本节课程中有一个课后习题,我觉得应该好好分析下其过程:
strtok 函数中有两个引用参数和一个引用返回值,按照上述的标注方式,返回值应该至少跟可变引用的生命周期一样长。
s 的生命周期 ‘a,从 1→4,s1 的生命周期 ‘b 从 2→4,hello 的生命周期 ‘c 从 3→4,即 ‘a > ‘b > ‘c。
s1 可变借用 &mut 的生命周期这里暂时记为 ‘d。
根据函数中的标注,’d 至少要跟返回值 hello 的生命周期 ‘c 一样长,即’d 从 3→4。
而第 4 行,println! 中又出现了 s1 的只读借用,可变借用与只读借用不可共存,所以报错。
解决办法也很简单,把 s1 与 hello 分开打印。
上面这个过程比较难以理解,我也是看了好多遍才逐渐明朗。
Rust 会尝试对函数中引用类型的参数和返回值进行生命周期推断,推断的规则如下:
所有引用类型的参数都有独立的生命周期 'a 、'b 等。
如果只有一个引用型输入,它的生命周期会赋给所有输出。
如果有多个引用类型的参数,其中一个是 self,那么它的生命周期会赋给所有输出。
本节课程链接:《10|生命周期:你创建的值究竟能活多久?》
历史文章推荐
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/7aa91cc790f5f3177db349560】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论