I agree that it would be easier to give meaningful help if we know what you are trying to make. There are more than one solution that all have trade-offs.
But just to try to explain the errors, let's look at a version of your function with explicit lifetime parameters added in:
struct Unit<'a> {
data: String,
ptr_data: Vec<&'a Self>,
}
struct UnitStorage <'a> {
storage: Vec<Unit<'a>>,
}
impl <'a> UnitStorage<'a> {
fn recusive<'self, 'unit>(&'self mut self, unit: &'unit mut Unit<'a>) {
let new_unit = Unit {data: "hello".to_owned(), ptr_data: vec![]};
self.storage.push(new_unit);
unit.push(self.storage[self.storage.len() - 1]);
}
}
This is pretty much how the borrow checker is treating it. You have the two lifetimes that I have named 'self
and 'unit
to show what they are borrowing from. They could be anything, as far as the compiler knows, and not necessarily the same as 'a
.
In your first two lines, you create a Unit
that, through inference, are set to hold references that's valid for the rest of 'a
. That's all good for now, since the ptr_data
is empty, but would also be fine if you added some explicitly 'a
marked references to it.
The problem occurs at the last line. You are borrowing from self.storage
with an even shorter lifetime (let's name it 'index
), that's at most valid for the rest of 'self
, where it was borrowed from. Note that this is not 'a
, just some lifetime that happens to exist during a period 'a
because 'self
happens to exist during a period of 'a
. But both 'index
and 'self
may end before 'a
ends. That's why it's not assignable as 'a
.
That's why it's complaining.
At this point it may be tempting to try fn recusive(&'a mut self, unit: &mut Unit<'a>)
, so let me continue the train of thought.
This would force the borrow to be valid for 'a
and will actually compile. We are not trying to pass anything off for what it isn't there, since everything is explicitly borrowed from 'a
. But we will not be able to use the function as you may expect.
This will not work, no matter how you turn it, since it requires two mutable references to storage
:
let mut storage = UnitStorage { storage: vec![] };
storage.storage.push(Unit { data: "first".to_owned(), ptr_data: vec![] });
storage.recusive(&mut storage.storage[0]); // borrows `storage` as mut twice here
This boils down to having UnitStoreage
store a reference to itself. That's instant headache in Rust because you can't make the &mut
borrow after the &
borrow you want to store.
Fun fact is that you can use the function if you can store the first Unit
somewhere else. Let's try with a separate Vec
. We can call it once:
let mut arena = Vec::new();
let mut storage = UnitStorage { storage: vec![] };
arena.push(Unit { data: "first".to_owned(), ptr_data: vec![] });
storage.recusive(&mut arena[0]);
...but we can't do it again because storage
is still borrowed by the first call:
let mut arena = Vec::new();
let mut storage = UnitStorage { storage: vec![] };
arena.push(Unit { data: "first".to_owned(), ptr_data: vec![] });
storage.recusive(&mut arena[0]);
storage.recusive(&mut arena[0]); // Already borrowed as mut!
In fact, storage
is completely inaccessible now while arena
is still around.
That's because when we are saying fn recusive(&'a mut self, unit: &mut Unit<'a>)
we are telling the compiler that a call to recusive
need to borrow self
for the remainder of 'a
to avoid invalid references, since everything we borrow from self
should be possible to store in unit
. And 'a
lasts for as long as arena
is around, since that's where we put the Unit
that now borrows from storage
.