Given the following code:
use core::{
marker::PhantomData,
mem::{self, MaybeUninit},
ptr,
};
pub trait Foo {
fn bar(&self);
}
struct VTable {
cast_ref: unsafe fn(*const ()) -> *const dyn Foo,
cast_mut: unsafe fn(*mut ()) -> *mut dyn Foo,
}
pub struct InlineDynFoo<T> {
vtable: &'static VTable,
storage: MaybeUninit<T>,
_marker: PhantomData<dyn Foo>,
}
impl<T> InlineDynFoo<T> {
pub fn try_from<U: Foo + 'static>(value: U) -> Result<Self, U> {
unsafe {
Self::with_vtable(
&VTable {
cast_ref: |x| x.cast::<U>(),
cast_mut: |x| x.cast::<U>(),
},
value,
)
}
}
unsafe fn with_vtable<U: 'static>(vtable: &'static VTable, value: U) -> Result<Self, U> {
if (mem::size_of::<U>() <= mem::size_of::<T>())
&& (mem::align_of::<U>() <= mem::align_of::<T>())
{
let mut storage = MaybeUninit::<T>::uninit();
storage.as_mut_ptr().cast::<U>().write(value);
Ok(Self {
vtable,
storage,
_marker: PhantomData,
})
} else {
Err(value)
}
}
pub fn get_ref(&self) -> &dyn Foo {
unsafe { &*(self.vtable.cast_ref)(self.storage.as_ptr().cast()) }
}
pub fn get_mut(&mut self) -> &mut dyn Foo {
unsafe { &mut *(self.vtable.cast_mut)(self.storage.as_mut_ptr().cast()) }
}
}
impl<T> Drop for InlineDynFoo<T> {
fn drop(&mut self) {
unsafe {
ptr::drop_in_place(self.get_mut());
}
}
}
Is there a type T
for which the code is unsound? If so, are there any types, other than the trivial case where T=U
, for which the code is sound?
Update:
When attempting to add some simplified test cases to the playground, I discovered that when run through miri an error is found for T=usize
; however, miri does not throw an error for the seemingly related case of T=[usize;1]
.