In general, I strongly advise against type punning on the pointee, while keeping Rust high level pointers around:
it is unsound to transmute a &mut Repr<()> to a &mut Repr<SomethingNot0SizedOrNot1Aligned>,
Ditto for the other high level pointers (e.g., & _, Box<_>)
More precisely: it is unsound to dereference any pointer that was created from casting to a Repr<T> a pointer that originated from a &[mut] Repr<()> . See pointer provenance and the following issue.
So it's at least a safety invariant. It may even be a validity invariant, but that part is not settled yet (see also the issue for Box).
In practice, thus, it is best to hard-code the pointer type (in this example, Box, inside the wrapper type (FileHandle), and only deal with raw pointers from that moment onwards:
pub
struct BoxedFileHandle {
ptr: ptr::NonNull<Repr<()>>, // represents a "Box<Repr<()>>",
}
impl BoxedFileHandle {
pub
fn for_writer<W : Write + 'static> (writer: W)
-> BoxedFileHandle
{
let repr: Repr<W> = Repr { vtable: ..., data: writer };
Self {
ptr: ptr::NonNull::from(Box::leak(Box::new(repr))).cast(),
}
}
}
impl Drop for BoxedFileHandle {
fn drop (self: &'_ mut BoxedFileHandle)
{
let VTable { destroy, .. } = self.vtable();
unsafe { destroy(self.ptr); } // assuming `destroy` also deallocates the Box
/* destroy would be `drop::<Box<Repr<OriginalType>>>(Box::from_raw( _ ))` */
}
}
impl BoxedFileHandle {
#[inline]
fn vtable (self: &'_ BoxedFileHandle)
-> VTable
{
// This is the only time we create a Rust high-level pointer
// to a `Repr<()>`. Although error-prone, here this is fine, because:
// - such high level pointer no longer exists by the time the function returns
// - Reading a `Repr<()>` out of a `Repr<W>` is well-defined (thanks to `#[repr(C)]`).
unsafe { self.ptr.as_ref().vtable }
}
pub
fn into_ffi (self: BoxedFileHandle)
-> *mut ::core::ffi::c_void
{
self.ptr.cast().as_ptr()
}
pub
unsafe
fn from_ffi (ptr: *mut ::core::ffi::c_void)
-> BoxedFileHandle
{
Self { ptr: ptr::NonNull::new(ptr.cast()).expect("Got NULL") }
}
}