Need help understanding lifetime mismatch

I'd appreciate some help in understanding the source of the compiler error regarding mismatched lifetimes. Here's the playground link.

I have the following structs:

enum Data<'a> {
    Complex(&'a Data<'a>)

struct Foo<'a> {
    table: HashMap<String, Box<Data<'a>>>,
    values: Vec<Bar<'a>>

struct Bar<'a> {
    data: &'a Data<'a>

Basically, the table owns the data (via Box) and the values vec contains references to some of them. Except, the compiler complains that it can't infer lifetimes properly when I try to insert data into values:

impl<'a> Foo<'a> {
    fn foo(&mut self) {
        let v = &self.table[&"xyz".to_string()];
        self.values.push(Bar{data: v});

As far as I can see, all the objects live under 'a, so what is the source of the error? Do I need a lifetime parameter for the &mut self too?

Hmm ok doing a &'a mut self worked. But I don't really understand why it's necessary. Doesn't the type of table already specify that its data will live for 'a?

You are trying to build a self-referential struct, i.e. a struct where one field has a reference into the struct itself. In this case the values field holds references into table, and also table holds references into table. Neither of these are allowed in safe Rust outside some edge cases that don't apply to you.

That's not what &'a mut self means when 'a is a lifetime on the struct. It means "borrow this struct until it goes out of scope", and once you've borrowed it mutably until the struct goes out of scope, you cannot access it again because any later access to the value would overlap with the mutable borrow, as such an access would necessarily happen before it goes out of scope.

Generally a lifetime on a struct should only be used to mark references to values stored outside the struct. This is why they show up in the type of the struct — they're there such that the lifetime can be tied to the owner of the value behind the reference.

Thanks for the explanation, I'm starting to understand some of it. I was trying to replicate the python model, where everything is a reference, but I guess that's too unsafe, and I'm gonna have to rely on heap allocations after all.

I still have some doubts regarding the lifetimes. You said:

What exactly is "scope" here? Is it the function scope of foo? If so, then why did borrowing using &mut self allow me to access self multiple times? My guess is &'a mut self means "this mutable reference is valid for all of 'a", which blocks further uses of that value. Is that correct?

I just mean until it is destroyed. Any lifetime on a struct must contain the entire region in which the struct exists, so to borrow it for at least such a region, you force the region in which the struct exists to be equal to the region of the lifetime.

Ah I get it now. That does sound a bit too much. I'll try to rethink the data model in a way that doesn't use so many references. Thanks for your prompt help. :slight_smile:

By the way, it might be useful to understand that Rust references are not at all similar to Python references. Essentially, in Python every reference type is like Rc<RefCell<T>> in Rust, i.e. in Rust terms it is owned, so that the data will not be cleared while at least one such reference to it exists.

Rust references are not about indirection - that's what Box, Rc and friends are for. Rust references are about temporarily locking the data - with either concurrent locks (immutable references) or exclusive locks (mutable ones). But the data to be locked must exist independently of the reference.

That is indeed quite a nice explanation. Thanks for elaborating on the details of "owning" the data, like the Python model.

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.