How does `Elsa`'s `FrozenVec<T>` work?

I'm going through the FrozenVec<T> implementation in elsa and I can't figure out how it's safe.

Internally a FrozenVec<T> is just a Vec<T>:

pub struct FrozenVec<T> {
    vec: UnsafeCell<Vec<T>>,
}

The idea behind the crate is that you can push a T with just a &FrozenVec<T>, but you can't get a &mut T back. This is the implementation:

    pub fn push_get(&self, val: T) -> &T::Target {
        unsafe {
            let vec = self.vec.get();
            (*vec).push(val);
            &*(&**(*vec).get_unchecked((*vec).len() - 1) as *const T::Target)
        }
    }

However my understanding was that, whenever Vec<T>'s size reached 2n + 1 where n is a natural number, the Vec<T> will have to reallocate and (potentially) move all of its items. This means that, if I push 16 items into a Vec<T> and then push one more, a reference to any one of those 16 items should be invalidated. Am I missing something here?

That definition is in an impl block which requires https://crates.io/crates/stable_deref_trait . e.g. Box. It returns a &T instead of &Box for that case. The box moves, but the inner type doesn't.

3 Likes

From elsa's doc :

This is safe because these collections only support storing data that’s present behind some indirection – i.e. String , Vec<T> , Box<T> , etc, and they only yield references to the data behind the allocation

1 Like

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.