Hi, I just wanted to ask whether the following container which I named DisjointVec, for the lack of a better name, is safe (if it violates any rust aliasing or other rules):
pub struct DisjointVec<T> {
v: UnsafeCell<Vec<Box<T>>>
}
impl<T> DisjointVec<T> {
fn get(&self, idx: usize) -> &T {
unsafe { &*(*self.v.get()).index(idx) }
}
fn get_mut(&mut self, idx: usize) -> &mut T {
self.v.get_mut().index_mut(idx)
}
fn push(&self, val: T) { // <- The immutable self is the most important part
unsafe { (*self.v.get()).push(Box::new(val)) }
}
}
My reasoning would be that the items aren't stored directly in the vector but indirectly via the Box. Therefore when a new element is pushed, the vector's inner allocation may be moved but the contents of the boxes stay at the same address, therefore they don't invalidate exising references.
Is this true or are there any other magic Rust reasons that would make this unsafe?
Physical addresses not changing is one thing, but Rust pointers have a nasty property called "provenance", which causes all sorts of "obvious" intuitions break down hopelessly. In this case, the provenance of the returned references is the box in which they are stored, i.e. the returned reference derives from the Box. I therefore think that if the Box itself is moved, then your reference is invalidated as well.
You might be able to work around this using raw pointers and Box::into_raw()/Box::from_raw(), because raw pointers have somewhat looser requirements than references, but I'm not nearly sure and I would advise against doing either that or what you are currently doing.
Instead, you could just put the inner Vec into a RefCell – it's a type that is specifically designed for solving this kind of problem.