参考章节《Rust 程序设计语言》第10.3章 生命周期确保引用有效
生命周期有什么用?答:避免出现悬垂引用
问题,那么问题来了,什么是悬垂引用
?
|
|
先说说看完书中这一章我自己的理解
- 生命周期是给谁看的?答:
编译器!
- 生命周期就好像一套
规则
,告诉编译器以这个规则给我检查代码是否满足要求
我们先来看这么一个例子
|
|
这段代码不能通过编译,原因是 r
引用的值(x
)在尝试使用之前就离开了作用域。
那么Rust是怎么知道的呢?答:这得益于Rust的借用检查器
我们来看看借用检查器都做了什么
还是上面的代码
|
|
书中这一段解释的非常清楚,所以我就抄过来了
这里将
r
的生命周期标记为'a
并将x
的生命周期标记为'b
。如你所见,内部的'b
块要比外部的生命周期'a
小得多。
在编译时,Rust 比较这两个生命周期的大小,并发现r
拥有生命周期'a
,不过它引用了一个拥有生命周期'b
的对象。
但因为生命周期'b
比生命周期'a
要小:被引用的对象比它的引用者存在的时间更短。所以程序被拒绝编译,
让我们来看一个没有产生悬垂引用且可以正确编译的例子
|
|
这里 x
拥有生命周期 'b
,比 'a
要大。这就意味着 r
可以引用 x
:Rust 知道 r
中的引用在 x
有效的时候也总是有效的。
当借用检查器无法帮助我们分析检查时
某些情况下,借用检查器无法帮助我们分析代码,这时候就需要我们手动
标记
一下,好让借用检查器按照我们的规则进行检查
那么什么情况下借用检查器无法帮助我们呢?
- 函数采用引用做参数,并且返回该引用,请看如下代码
|
|
借用检查器不知道传入的引用的具体生命周期,因为 x
和 y
在哪定义的不清楚嘛
由于不清楚 x
和 y
的生命周期,因此借用检查器就无法让它们和 r
的生命周期进行比较,所以这段代码不允许编译
好了,我们知道了原因,接下来看看如何解决它
函数中的泛型生命周期
我们可以通过一个叫作生命周期注解
的东西,来手动标记
一下,注意,它并不会改变生命周期
,而是告诉借用检查器
,我要求的生命周期
生命周期注解
有着一个不太常见的语法:生命周期参数名称必须以撇号('
)开头,其名称通常全是小写
,类似于泛型其名称非常短。
'a
是大多数人默认使用的名称。生命周期参数注解位于引用的&
之后,并有一个空格
来将引用类型与生命周期注解分隔开。
下面的代码,修复了上面的问题
|
|
这段代码,指明了 x
必须拥有生命周期 'a
,并且 y
也必须拥有生命周期 'a
,这也就意味着,x
和 y
必须处在同一个生命周期
然后是返回值也指明了必须拥有生命周期 'a
,也就意味着,返回值必须是 x
或 y
中的一个
这样的话,当传入的 x
和 y
不在同一个生命周期中时,编译将不被通过
深入理解生命周期
指定生命周期参数的正确方式依赖函数实现的具体功能。
书中这一小节也讲的非常清楚,所以我就直接抄过来了
例如,如果将
longest
函数的实现修改为总是返回第一个参数而不是最长的字符串slice
,就不需要为参数y
指定一个生命周期。
例如,如下代码将能够编译:
|
|
在这个例子中,我们为参数
x
和返回值指定了生命周期参数'a
,不过没有为参数y
指定,因为y
的生命周期与参数x
和返回值的生命周期没有任何关系。
当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。
如果返回的引用没有指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值,那么它将会是一个悬垂引用,因为它将会在函数结束时离开作用域。
如果你非要返回一个没有指向任何一个参数的引用,那么你需要指明 'static
生命周期,这个后面我们会讲到
如果你非要返回一个没有指向任何一个参数的引用,那么你必须返回一个具有静态生命周期
的引用,光指明 'static
是没有用的,因为生命周期注解不改变实际的生命周期
结构体定义中的生命周期注解
|
|
这个注解意味着
ImportantExcerpt
的实例不能比其part
字段中的引用存在的更久,所以ImportantExcerpt
实例中的引用总是有效的。
这一节,书中我认为没有讲清楚,为什么注解字段后,实例就不能比字段存在的久了?
它们是怎么比较的,难道指明了生命周期注解后字段的生命周期就比实例的生命周期大?
那么如果没有显示指明字段生命周期,是不是字段的生命周期就比实例的生命周期小?
我是这么理解的,如果字段没有显示的指明生命周期,那么字段是属于实例的,它和实例属于同一个生命周期
如果显示指明了生命周期,则字段的生命周期就会套在实例的生命周期外面,这里就死记硬背
吧
生命周期省略(Lifetime Elision)
关于生命周期省略,我个人是不建议省略写法的,第一是记规则比较麻烦,第二是省略了会造成代码不明确,不过感兴趣你可以参考这一节生命周期省略
在方法中定义生命周期
在方法中也可以定义生命周期,只需要在 impl
关键词和 类型
后面声明生命周期即可,因为这些生命周期是结构体类型的一部分
|
|
静态生命周期
所有的字符串字面值都是 'static
的。
|
|