Search code examples
rustunsafe

Is it possible to have a generic Rust T be always heap allocated?


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) }).


Solution

  • 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.