Diffstat (limited to 'src/any/erased.rs')
| -rw-r--r-- | src/any/erased.rs | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/src/any/erased.rs b/src/any/erased.rs new file mode 100644 index 0000000..65571b1 --- /dev/null +++ b/src/any/erased.rs @@ -0,0 +1,65 @@ +use core::{ + marker::PhantomData, + mem::{ManuallyDrop, MaybeUninit}, +}; + +/// Tiny version of dungeon-cell's DungeonCore. +/// +/// Can be used to erase types. This container **will not** +/// run the stored value's Drop impl. Also this container does not +/// track what type it is storing. Also, also this container +/// does not align it's value so no making borrows of the inner value!. +/// +/// This type **is** safe to use in const environments. +pub struct Erased<const N: usize> { + /// A value of some unknown type that is N bytes long. + /// + /// We don't need a UnsafeCell here because we never give out a borrow + /// of the value. + bytes: MaybeUninit<[u8; N]>, + + _marker: PhantomData<*const ()>, +} + +impl<const N: usize> Erased<N> { + /// Erase the type of a value. + pub const fn new<T>(value: T) -> Self { + const { + assert!(core::mem::size_of::<T>() <= N); + } + + let value = ManuallyDrop::new(value); + + #[repr(C)] + union Transmute<T, const N: usize> { + value: ManuallyDrop<T>, + bytes: MaybeUninit<[u8; N]>, + } + + Self { + #[allow(unsafe_code)] + bytes: unsafe { Transmute { value }.bytes }, + _marker: PhantomData, + } + } + + /// Extract the original value. + /// + /// # Safety + /// `T` must be the same type used when [`Self::new`] was called. + #[allow(unsafe_code)] + pub const unsafe fn into_inner<T>(self) -> T { + const { + // This should already be covered by new, but here for completeness. + assert!(core::mem::size_of::<T>() <= N); + } + + #[repr(C)] + union Transmute<T, const N: usize> { + bytes: MaybeUninit<[u8; N]>, + value: ManuallyDrop<T>, + } + + ManuallyDrop::into_inner(unsafe { Transmute { bytes: self.bytes }.value }) + } +} |