Rust为什么不能在同一Struct中存储值和对该值的引用?


我们看一下这个的简单实现

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中存储值和对该值的引用。
将拥有数据的类型放在一个结构中,然后提供允许您根据需要获取引用或包含引用的对象的方法。