Struct Parent/ Member Lifetime Conflicts

Here is a situation I imagine comes up often, and I haven't been able to get lifetime annotations consistent with it:

  • A Parent struct has a Vec of Child struct-instances
  • Each Child has a reference to some other data from Parent
  • Each Parents children-vector is initially empty, then Children are created and added to it

Simplified version, in which the sole Child member is a string-reference to its Parent's (string) name. (Real cases of course have many more members.)

struct Child<'a> {
    parents_name: &'a str,
}

impl<'a> Child<'a> {
    fn new(p: &'a Parent) -> Self {
        Child {parents_name: &p.name}
    }
}

struct Parent<'a> {
    name: String, 
    children: Vec<Child<'a>>,
}

impl<'a> Parent<'a> {
    fn new() -> Self {
        Parent {
            name: "something".to_string(),
            children: vec![],
        }
    }
    fn add(&mut self) {
        let c = Child::new(self);
        self.children.push(c);
    }
}

fn main() {
    let mut p = Parent::new();
    p.add();
}

Generates these errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/main.rs:22:17
   |
22 |         let c = Child::new(self);
   |                 ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 21:5...
  --> src/main.rs:21:5
   |
21 | /     fn add(&mut self) {
22 | |         let c = Child::new(self);
23 | |         self.children.push(c);
24 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:22:28
   |
22 |         let c = Child::new(self);
   |                            ^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 14:6...
  --> src/main.rs:14:6
   |
14 | impl<'a> Parent<'a> {
   |      ^^
note: ...so that the expression is assignable
  --> src/main.rs:23:28
   |
23 |         self.children.push(c);
   |                            ^
   = note: expected `Child<'a>`
              found `Child<'_>`

error: aborting due to previous error

Playground: Rust Playground

Each Child clearly does not live "as long as" (perhaps more accurately "as early as") its Parent. But the references to parents_name of course life exactly as long as the Parent.
Nonetheless it doesn't seem a single lifetime specifier 'a for both Parent and Child covers this.

What would make this work? Some combination of separate lifetimes for Parent and Child, and constraints between them?

1 Like

This is one of the main uses of Rc/Arc. They let the parent_name string be jointly owned between the two structs, and it will last as long as either struct needs it.

&'lifetime references are only really designed for temporary borrowing, like you might want for a function argument. Storing them in structs is an advanced technique, and rarely the best way to do things.

The errors relating to the "outlived by" relationship between parent and child lifetimes, are kind of misdirecting you from the actual problem, If you add a lifetime fn add(&'a mut self) {, instead of inferring it. You'll get a much more helpful error.

error[E0502]: cannot borrow `self.children` as mutable because it is also borrowed as immutable
  --> src/main.rs:26:9
   |
17 | impl<'a> Parent<'a> {
   |      -- lifetime `'a` defined here
...
25 |         let c = Child::new(self);
   |                 ----------------
   |                 |          |
   |                 |          immutable borrow occurs here
   |                 argument requires that `*self` is borrowed for `'a`
26 |         self.children.push(c);
   |         ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

No matter the lifetime "outlived by" relationship of parent outliving child, you can't have both an immutable borrow of parent, and a mutable borrow of parent at the same time. One way to sidestep this you can do is split Parent up by adding a data structure which is immutably borrowed instead of self, once that is done you should be able to borrow self mutably still.

struct Parent<'a> {
    data: ParentData,
    children: Vec<Child<'a>>,
}

struct ParentData {
    name: String,
}

impl<'a> Child<'a> {
    fn new(p: &'a ParentData) -> Self {
        Child {parents_name: &p.name}
    }
}

@ratmice is right about the reason for why this doesn't work: you can't mutably borrow Parent (to do self.children.push()) because you must first borrow self immutably (to create the new Child). There's good reason for this: the Childs each hold a reference to the String in the struct. But if someone borrowed the struct mutably, they could call String::push on name and if/when the String reallocates its buffer, all the Childs are now left with dangling references.
But if I understand his solution correctly, his solution doesn't quite work.

&'a mut self isn't the inferred lifetime. I'll quote this great post about lifetime misconceptions for why it doesn't work:

If we have some struct generic over 'a we almost never want to write a method with a &'a mut self receiver. What we're communicating to Rust is "this method will mutably borrow the struct for the entirety of the struct's lifetime" . In practice this means Rust's borrow checker will only allow at most one call to some_method before the struct becomes permanently mutably borrowed and thus unusable.

So if you call p.add() anywhere (playground), and then try to do anything with p, you get

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable
  --> src/main.rs:34:10
   |
33 |     p.add();
   |     - mutable borrow occurs here
34 |     dbg!(&p);
   |          ^^
   |          |
   |          immutable borrow occurs here
   |          mutable borrow later used here

But his solution is a step in the right direction. If you access a struct's field instead of using a method, it'll borrow only that specific field instead of the whole struct.
So, for convenience, I'll first change Child::new to:

impl<'a> Child<'a> {
    fn new(parents_name: &'a str) -> Self {
        Child { parents_name }
    }
}
struct Parent<'a> {
    name: String, 
    children: Vec<Child<'a>>,
}

And then in main we can do:

fn main() {
    let mut p = Parent::new();
    p.children.push(Child::new(&p.name));
    p.children.push(Child::new(&p.name));
}

This works because it borrows p.name immutably, but not the entire struct immutably. Likewise, p.children is borrowed mutably, but not the entire struct. This behavior is unique to accessing struct fields and is lost when you use methods. fn add(&mut self) borrows the entire struct mutably. This behavior is called Splitting Borrows if you wanna know why it works this way.

Soo... voila! Except not really. I think this feels janky, especially since it would only work in other modules if you made name and children both public.
If you or your API can help it, I think it'd be best to keep the data, like name and company, all in 1 struct, and have the Vec<Child> outside of that struct in a variable. If needed, they can be passed to other places together in a tuple.

If that's not possible, then @2e71828's suggestion is good. Either use Rc<T> for the Parent and Child if you wanna keep it simple, or use Rc<T> for the parent struct with all the data, and rc::Weak<T> for the Child structs.

1 Like

How do you use the children? Do you only need to iterate over them via the parent. Then you cou could add a method child_iter_mut(&mut self), that returns an iterator over a struct containing the child data + reference to the parent data. The trick is to use 2 Child structs. The internal one doesn't contain any references. When you iterate over all children You construct an ExternalChild:

struct ExternalChild<'a> {
    parent_name: &'a mut String,
    child: &'a mut InternalChild,
}

This is possible, because you can deconstruct &mut Parent into its parts by doing:

// impl Parent
pub fn my_fn(&mut self) {
    let Self { name, children } = self;
    // name: &mut String
    // children: &mut Vec<InternalChild>
}

The above example may throw errors. I only ever had to deconstruct a struct once. Might require a bit more fiddling.

EDIT: I just remembered, you may not be able to return an Iterator, but have to implement something like a custom Iterator without next, because you could keep several ExternalChilds alive at the same time, which would throw a lifetime error, because then you would mutably borrow the parent name more than once, which isn't possible. The custom Iterator would then be usable through a for_each method which operates on a closure, making sure you cannot have more than a single external child alive at any time.

Alternatively, if you only need shared references to the parent data, it might be possible with a bit of reborrowing, but that's stepping into territory I haven't explored myself enough to provide any guarantees about.

EDIT2: You could use a RefCell to store the mutably borrowed parent data, if you need to keep several children alive at the same time and you need the parent data to be mutable, too. If reborrowing doesn't work for the shared read-only parent data version, a RefCell would work for that, as well. I just tested that in the playground:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.