Why does inner variable borrow outer variable for longer than the inner variable is in scope?

52 views Asked by At

I had the idea to use a pinus::sync::PineMap (GitHub) to make all references of equivalent objects actually reference the same object in memory (the "one" object would be a PineMap-owned value). I'm trying out PineMap for this because its insert will not move its items in memory (its insert borrows &self not &mut self too) so references to its values will stay valid even when adding more entries to the PineMap, and I can build self-referential items.

I have some kind of lifetimes issue:

#[derive(Eq, PartialEq, Ord, PartialOrd)]
enum List<'a> {
    Cons(isize, &'a List<'a>),
    Nil,
}

fn main() {
    use List::*;
    use pinus::{prelude::*, sync::PineMap};
    let mut table = PineMap::new();
    table.insert(Nil, Nil);
    {
        let nil = table.get(&Nil).unwrap();
        table.insert(Cons(1, nil), Cons(1, nil));
    }
    table.clear();
}
error[E0597]: `table` does not live long enough
  --> src/main.rs:13:19
   |
13 |         let nil = table.get(&Nil).unwrap();
   |                   ^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
17 | }
   | -
   | |
   | `table` dropped here while still borrowed
   | borrow might be used here, when `table` is dropped and runs the `Drop` code for type `PineMap`

error[E0502]: cannot borrow `table` as mutable because it is also borrowed as immutable
  --> src/main.rs:16:5
   |
13 |         let nil = table.get(&Nil).unwrap();
   |                   --------------- immutable borrow occurs here
...
16 |     table.clear();
   |     ^^^^^^^^^^^^^ mutable borrow occurs here
17 | }
   | - immutable borrow might be used here, when `table` is dropped and runs the `Drop` code for type `PineMap`

I thought declaring nil in an inner scope would have resolved all my lifetime problems, because I understand this makes it have a shorter lifetime than table, so it shouldn't be borrowing table anymore by the time table itself goes out of scope.

Why does it look like the inner variable is borrowing an outer variable for longer than the inner variable is in scope?

And in general if this is an unfixable approach, how might I be able to solve my original problem of collecting lots of references to the same object based on object equivalency? If an object O is created, I want to look up "has O been seen before?" If it has, then get a reference to the "cached" O, if it has not, then cache it and be the first to get a reference to the newly cached object.

1

There are 1 answers

3
cafce25 On

The problem is you're making table self referential. Inserting a reference to table (nil) into table here,

let nil = table.get(&Nil).unwrap();
table.insert(Cons(1, nil), Cons(1, nil));

Means from this point onwards the borrow checker has to enforce that table outlives table which obviously is impossible.

You can't call any of

table.clear();
table.drop_entry();

to get out of this mess either since both need exclusive access to table (they take &mut self) and for as long as there is a shared reference to table inside of itself the borrow checker is not going to allow it.