use std::ops::Deref;
struct Test {
last: &'static str,
list: Vec<String>,
}
// This is safe because we own the value and will not modify it.
// When dropping, `last` will be dropped without freeing the memory.
impl Test {
pub fn new(value: String) -> Self {
Self {
last: unsafe { std::mem::transmute(value.deref()) },
list: vec![value],
}
}
pub fn insert(&mut self, value: String) {
self.last = unsafe { std::mem::transmute(value.deref()) };
self.list.push(value);
}
pub fn last(&self) -> &str {
self.last
}
}
Is it possible to do a similar construct with generics, something like Test<T:Deref>
? I am not sure deref()
guarantees any "permanent" heap address (in the limited context of my implementation), or does it?
The above code seems safe because calling deref()
on a String
gives a wide pointer to heap, and that address would never change unless the string itself is altered or the ownership is returned to the caller.
Note that I need to store &str
for my use cases rather than use ManuallyDrop<String>
created with ManuallyDrop::new(unsafe { ptr::read(&value) })
.
As you suspect, this is not safe.
There exists the stable_deref_trait
that provides the StableDeref
trait that is supposed to be used for such use-case. However, this crate is known to be unsound (or at least, used in unsound ways), since, for example, it implements StableDeref
for Box
but it is not clear whether it is okay to move a Box
while there are references to it (under Stacked Borrows, this is UB, and Miri will flag it as such).
If you want to be 100% safe, heap-allocate all stored objects and store them as *mut T
(not Box<T>
, for the reason mentioned above), then you can store references to them within the same struct.