1
votes

I am learning Rust and facing a compilation issue. I simplified my code (so it does not make any sense as it is right now) to post my problem here.

I have a Parent structure filled with a ChildWrapper and a Vec<&Child>. But the compiler won't let me set the reference, as the created reference would not necessarily outlive its content. To my eyes setting lifetimes here (pub fn get<'a>(&'a self) -> &'a Child {) should reassure the compiler the ref will live long enough... but, nah.

If I take ChildWrapper out of Parent I can make it work but still, I don't understand what is not ok in my code.

struct Child {
    e: bool,
}

impl Child {
    pub fn new() -> Child {
        Child { e: true }
    }
}

struct ChildWrapper {
    child: Child,
}

impl ChildWrapper {
    pub fn new() -> ChildWrapper {
        ChildWrapper { child: Child::new() }
    }

    pub fn get<'a>(&'a self) -> &'a Child {
        &self.child
    }
}

struct Parent<'a> {
    child_ref: Vec<&'a Child>,
    wrapper: ChildWrapper,
}

impl<'a> Parent<'a> {
    pub fn new() -> Parent<'a> {
        Parent {
            child_ref : Vec::new(),
            wrapper: ChildWrapper::new(),
        }
    }

    pub fn set_child_ref(&mut self) {
        self.child_ref.push(self.wrapper.get());
    }
}

fn main() {
    let mut parent = Parent::new();
    parent.set_child_ref();
}

And this is the error I am getting :

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:41:36
   |
41 |         self.child_ref.push(self.wrapper.get());
   |                                          ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 40:2...
  --> src/main.rs:40:2
   |
40 |       pub fn set_child_ref(&mut self) {
   |  _____^
41 | |         self.child_ref.push(self.wrapper.get());
42 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:41:23
   |
41 |         self.child_ref.push(self.wrapper.get());
   |                             ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 32:6...
  --> src/main.rs:32:6
   |
32 | impl<'a> Parent<'a> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:41:23
   |
41 |         self.child_ref.push(self.wrapper.get());
   |                             ^^^^^^^^^^^^^^^^^^

I know how to workaround the issue, and it probably is the sign of a bad design (or maybe not ?) but I am looking for an explanation and eventually a solution using this same architecture... Like how to explicitly set the correct lifetime for self.wrapper.get() ?

Thank you!

1
Actually self-response... I've been fighting with it for hours and found a fix right after posting. Maybe it will be useful for someone else :pub fn set_child_ref(&mut self) { => pub fn set_child_ref(&'a mut self) { I am not sure why but this lifetime had to be explicit. Feel free to give further explanations !gpoblon
&'a mut self, when 'a is a parameter of Self, is almost always a mistake. In this case it works, but you have made it impossible to do anything else with parent, because it's now permanently (and uniquely) borrowed. The best solution to this kind of problem is to avoid self-referential structs. Why can't I store a value and a reference to that value in the same struct? has further explanation and suggestions.trentcl

1 Answers

2
votes

To my eyes setting lifetimes here (pub fn get<'a>(&'a self) -> &'a Child {) should reassure the compiler the ref will live long enough... but, nah.

The lifetime annotations you've provided there are exactly what the Rust compiler would infer if you elided them, so this wouldn't make any difference.

As written, your set_child_ref method has elided the lifetimes, which means the inferred lifetimes are like this:

impl<'a> Parent<'a> {
    pub fn set_child_ref<'b>(&'b mut self) {
        self.child_ref.push(self.wrapper.get());
    }
}

This won't work. The lifetime 'b will be determined by how the caller of set_child_ref uses the parameter. The reference to self therefore could be shorter than the lifetime 'a. This would cause a problem because the reference to ChildWrapper, that you are storing in child_ref, is borrowed from that (potentially shorter-lived) reference.

The easy fix is to say that the reference to self must outlive 'a, which is the lifetime of the references in the child_ref vector:

impl<'a> Parent<'a> {
    pub fn set_child_ref(&'a mut self) {
        self.child_ref.push(self.wrapper.get());
    }
}