我们看一下这个的简单实现:
struct Parent { count: u32, }
struct Child<'a> { parent: &'a Parent, }
struct Combined<'a> { parent: Parent, child: Child<'a>, }
impl<'a> Combined<'a> { fn new() -> Self { let parent = Parent { count: 42 }; let child = Child { parent: &parent };
Combined { parent, child } } }
fn main() {}
|
出现错误:
error[E0515]: cannot return value referencing local variable <code>parent</code> --> src/main.rs:19:9 | 17 | let child = Child { parent: &parent }; | ------- <code>parent</code> is borrowed here 18 | 19 | Combined { parent, child } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0505]: cannot move out of <code>parent</code> because it is borrowed --> src/main.rs:19:20 | 14 | impl<'a> Combined<'a> { | -- lifetime <code>'a</code> defined here ... 17 | let child = Child { parent: &parent }; | ------- borrow of <code>parent</code> occurs here 18 | 19 | Combined { parent, child } | -----------^^^^^^--------- | | | | | move out of <code>parent</code> occurs here | returning this value requires that <code>parent</code> is borrowed for <code>'a</code>
|
为了完全理解这个错误,你必须思考数值在内存中是如何表示的,以及当你移动这些数值时会发生什么。
这正是Rust生命期/生存期(lifetimes)所要防止的问题。
生命期是一个元数据,它允许你和编译器知道一个值在其当前内存位置的有效时间。这是一个重要的区别,因为这是Rust新手常犯的一个错误。
Rust的生命期/生存期并不是指一个对象被创建和被销毁之间的时间段
打个比方,可以这样想:在一个人的一生中,他们会居住在许多不同的地方,每个地方都有一个不同的地址。锈的一生关注的是你目前居住的地址,而不是你将来什么时候死(尽管死亡也会改变你的地址)。每次你搬家都是相关的,因为你的地址不再有效了。
同样重要的是要注意,生命期不会改变你的代码;你的代码控制生命期,你的生命期不控制代码。
精辟的说法是 "生命期是描述性的,不是规定性的"。
让我们用一些行号来注释Combined::new,我们将用这些行号来强调生命期:
{ // 0 let parent = Parent { count: 42 }; // 1 let child = Child { parent: &parent }; // 2 // 3 Combined { parent, child } // 4 } // 5
|
- parent 的具体生存期是从1到4,包括在内(我将其表示为[1,4])。
- child的具体生命周期/生存期是[2,4],
- 而返回return 值的具体生命周期是[4,5]。
请注意,child本身的生存期是[2,4],但它引用的是一个生存期为[1,4]的值。只要引用值在引用值失效之前失效就可以。
问题出在:我们试图从块{}中返回return child时,而返回return 值的具体生命周期是[4,5],多了一个5,这将 "过度延长 "child生命周期[2,4],超过其自然生存期长度。
如何解决?
最简单和最推荐的解决方案是不要将这些东西放在同一结构中:
不要在同一Struct中存储值和对该值的引用。
将拥有数据的类型放在一个结构中,然后提供允许您根据需要获取引用或包含引用的对象的方法。