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_traitthat provides theStableDereftrait 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 implementsStableDerefforBoxbut it is not clear whether it is okay to move aBoxwhile 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(notBox<T>, for the reason mentioned above), then you can store references to them within the same struct.