I'm trying to create a data structure in which to store cached strings. My inclination is to store the strings in a Vec<String>
and push new allocations to the end of the vector, returning an immutable &str
reference as follows:
#[derive(Default)]
pub struct StringArena {
allocs: UnsafeCell<Vec<String>>,
}
impl StringArena {
pub fn new() -> Self {
Self::default()
}
pub fn push(&self, s: String) -> &str {
let allocs = self.allocs.get();
unsafe {
(&mut *allocs).push(s);
let len = (&*allocs).len();
(&*allocs).get_unchecked(len).deref()
}
}
}
However, I'm not sure if this is a sound use of UnsafeCell
since I am creating a mutable reference to the cell's interior while outstanding immutable &str
borrows of the individual strings exist.
For instance, in the following code, the second call to arena.push(...)
mutates the vector while str1
exists:
let arena = StringArena::new();
let str1 = arena.push("Hello".to_string());
let str2 = arena.push("World".to_string());
I am inclined to think my code is sound since calls push(...)
don't directly affect existing &str
references, as long as I only access the vector through this API. However, I don't know the specifics of Rust's aliasing rules, so I would appreciate clarification on whether my code invokes undefined behavior. Thank you!