Let's say I have a trait object dyn MyTrait
that I know is safe to move, and want to move it to a new memory location (a [NonNull<()>
] or *mut ()
that I could have allocated with std::alloc::alloc()
), and then have a pointer NonNull<dyn MyTrait>
to the recently moved object. How can I go from a NonNull<()>
to a NonNull<dyn MyTrait>
in stable Rust?
// Definitely won't work on stable Rust
fn do_magic(pointer: NonNull<()>, vtable: ???) -> NonNull<dyn MyTrait>;
With Nightly Rust
strict_provenance
has thestd::ptr::metadata()
andstd::ptr::from_raw_parts()
that can be used to make a*const MyTrait
with a specific VTable- Unfortunately, it's unlikely to come out soon, and its stable polyfill hasn't been updated in a year and it does not provide pointer metadata functions.
trait_upcasting
andcoerce_unsized
might be helpful here.
With Stable Rust
I had an idea to use traits that convert the NonNull<()>
to a NonNull<Self>
in order to "get" the correct VTable, something like:
use std::ptr::NonNull;
trait MyTrait {
fn my_trait(&self);
}
trait Reinterpret {
fn reinterpret(pointer: NonNull<()>) -> NonNull<dyn MyTrait>;
}
impl<T: MyTrait + 'static> Reinterpret for T {
fn reinterpret(pointer: NonNull<()>) -> NonNull<dyn MyTrait> {
// Note that here, T: Sized, which allows this cast to work
pointer.cast::<T>() as NonNull<dyn MyTrait>
}
}
// But how?
fn magic(source: NonNull<dyn MyTrait>, pointer_to_convert: NonNull<()>) -> NonNull<dyn MyTrait> {
todo!()
}
Unfortunately, I couldn't figure out how to get the trait strategy above to work with dyn MyTrait
(e.g. having a &self
parameter so Reinterpret
could be a trait object):
use std::ptr::NonNull;
trait MyTrait {
fn my_trait(&self);
}
trait Cast {
fn cast(&self, pointer_to_convert: NonNull<()>) -> NonNull<dyn MyTrait>;
}
impl<T: MyTrait + 'static> Cast for T {
fn cast(&self, pointer_to_convert: NonNull<()>) -> NonNull<dyn MyTrait> {
// Note that here, T: Sized, which allows this cast to work
pointer_to_convert.cast::<T>() as NonNull<dyn MyTrait>
}
}
// This is required, since Cast is not implemented for any Unsized types
impl Cast for dyn MyTrait {
fn cast(&self, pointer_to_convert: NonNull<()>) -> NonNull<dyn MyTrait> {
// Can't get VTable at all, back where we started
todo!()
}
}
fn magic(source: &dyn MyTrait, pointer_to_convert: NonNull<()>) -> NonNull<dyn MyTrait> {
// Fails to compile, 'static lifetime requirement!?
source.cast(pointer_to_convert)
}
In the above attempt, problems with trait lifetimes appeared, similar to the ones below:
(Just Google "rust trait requires self to be borrowed for static" or something similar)
Workarounds
- Make a custom struct for
dyn MyTrait
, like withstd::task::RawWaker
?- A struct could store a cast
fn
and a pointer to the trait object:struct MyTraitRef { cast: unsafe fn(NonNull<()>) -> NonNull<dyn MyTrait>, object: NonNull<()>, } impl Deref<dyn MyTrait> for MyTraitRef { type Target = dyn MyTrait; fn deref(&self) -> &dyn MyTrait { // Safety: Who knows? // Safety: Assumes that the cast function is getting a pointer to an object of the correct type unsafe { (self.cast)(self.object).as_mut() } } }
- This means a new struct might have to be made for every trait I want to work with, unless
MyTraitRef<Trait: ?Sized>
works.
- A struct could store a cast
- Transmute
NonNull<dyn MyTrait>
/&dyn MyTrait
/*const MyTrait
into a[*const (); 2]
and do war crimes to keep the VTable pointer and change the address (Literally Undefined Behavior, since the layout ofdyn MyTrait
is not stable)
Thanks for the help and suggestions!