Diffstat (limited to 'src/any/indirect.rs')
-rw-r--r--src/any/indirect.rs237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/any/indirect.rs b/src/any/indirect.rs
new file mode 100644
index 0000000..eef24f0
--- /dev/null
+++ b/src/any/indirect.rs
@@ -0,0 +1,237 @@
+//! Generic over borrow mutability.
+
+use core::mem::{ManuallyDrop, MaybeUninit};
+
+/// A higher kinded type.
+pub trait HigherKinded<'a> {
+ /// The type with `T` applied.
+ type ForT<T: ?Sized + 'a>: Unpin + 'a;
+}
+
+/// A pointer like type.
+///
+/// Allows being generic over `&T` and `&mut T`.
+///
+/// Implemented by marker types [`Ref`] and [`Mut`].
+pub trait Indirect<'a>: HigherKinded<'a> + sealed::Sealed<'a> {}
+
+pub(super) mod sealed {
+ use core::marker::PhantomData;
+
+ use super::*;
+
+ /// Sealed trait to prevent users from implementing Indirect and provides conversion
+ /// to and from a raw indirect.
+ pub trait Sealed<'a>: HigherKinded<'a> + Sized {
+ /// Convert the pointer into a raw indirection.
+ fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]>;
+
+ /// Convert a raw indirection back into the pointer.
+ ///
+ /// # Safety
+ /// `value` must have been created by `Self::into_raw`.
+ /// This function must not be called twice for the same `value`.
+ /// The type `T` must be the one given to `Self::into_raw`.
+ unsafe fn from_raw<T: ?Sized + 'a>(value: MaybeUninit<[u8; INDIRECT_SIZE]>) -> Self::ForT<T>;
+ }
+
+ /// An opaque set of bytes the size of a fat pointer.
+ ///
+ /// Repr wise this is exactly `MaybeUninit<[u8; { size of a fat pointer }]>`.
+ #[repr(transparent)]
+ pub struct RawIndirect<'a, I> {
+ indirect: MaybeUninit<[u8; INDIRECT_SIZE]>,
+ _marker: PhantomData<fn(&'a ()) -> (&'a (), I)>,
+ }
+
+ impl<'a, I: Indirect<'a>> RawIndirect<'a, I> {
+ pub fn new<T: ?Sized>(indirect: I::ForT<T>) -> Self {
+ Self {
+ indirect: I::into_raw(indirect),
+ _marker: PhantomData,
+ }
+ }
+
+ /// # Safety
+ /// The type `T` must be the same one used in `Self::new`.
+ pub unsafe fn into_inner<T: ?Sized>(self) -> I::ForT<T> {
+ // SAFETY: indirect was created with this I's into_raw in Self::new.
+ // This function cannot be called twice because we take ownership of self and
+ // this type is not clone or copy.
+ // The caller makes sure that the T is the same.
+ // Also the lifetime 'a must be the same because Self is invariant over it.
+ unsafe {
+ I::from_raw(self.indirect)
+ }
+ }
+ }
+
+ /// Size of a fat pointer.
+ const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2;
+
+ /// Helper trait for double checking the sizes of fat pointers.
+ trait Helper {
+ fn run(&self);
+ }
+
+ const _: () =
+ assert!(core::mem::size_of::<&dyn Helper>() <= core::mem::size_of::<RawIndirect<'_, Ref>>());
+
+ // Borrow doesn't need to be dropped.
+ const _: () =
+ assert!(!core::mem::needs_drop::<&i32>());
+
+ const _: () = assert!(
+ core::mem::size_of::<&mut dyn Helper>() <= core::mem::size_of::<sealed::RawIndirect<'_, Mut>>()
+ );
+
+ // Mutable borrow doesn't need to be dropped.
+ const _: () =
+ assert!(!core::mem::needs_drop::<&mut i32>());
+
+ impl<'a> Sealed<'a> for Ref {
+ fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]> {
+ // SAFETY: A possibly fat borrow can be stores in a 2 usize wide maybe uninit array.
+ unsafe { transmute::<&'a T, MaybeUninit<[u8; INDIRECT_SIZE]>>(value) }
+ }
+
+ unsafe fn from_raw<T: ?Sized + 'a>(any: MaybeUninit<[u8; INDIRECT_SIZE]>) -> Self::ForT<T> {
+ // SAFETY: We know the value is from Self::into_raw because of the caller invariants.
+ unsafe { transmute::<MaybeUninit<[u8; INDIRECT_SIZE]>, &'a T>(any) }
+ }
+ }
+
+ impl<'a> Sealed<'a> for Mut {
+ fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]> {
+ // SAFETY: A possibly fat borrow can be stores in a 2 usize wide maybe uninit array.
+ unsafe { transmute::<&'a mut T, MaybeUninit<[u8; INDIRECT_SIZE]>>(value) }
+ }
+
+ unsafe fn from_raw<T: ?Sized + 'a>(any: MaybeUninit<[u8; INDIRECT_SIZE]>) -> Self::ForT<T> {
+ // SAFETY: We know the value is from Self::into_raw because of the caller invariants.
+ unsafe { transmute::<MaybeUninit<[u8; INDIRECT_SIZE]>, &'a mut T>(any) }
+ }
+ }
+}
+
+/// Marker type for [`AnyTraitObject`](super::AnyTraitObject) for a borrow `&T`.
+pub enum Ref {}
+
+impl<'a> HigherKinded<'a> for Ref {
+ type ForT<T: ?Sized + 'a> = &'a T;
+}
+
+impl<'a> Indirect<'a> for Ref {}
+
+/// Marker type for [`AnyTraitObject`](super::AnyTraitObject) for a mutable borrow `&mut T`.
+pub enum Mut {}
+
+impl<'a> HigherKinded<'a> for Mut {
+ type ForT<T: ?Sized + 'a> = &'a mut T;
+}
+
+impl<'a> Indirect<'a> for Mut {}
+
+/// # Safety
+/// Same rules as [`core::mem::transmute()`].
+unsafe fn transmute<T, U>(value: T) -> U {
+ // Create union type that can store a `T` or a `U`.
+ // We can then use this to convert between them.
+ //
+ // The repr(C) layout forces no offset between `t` and `u` as talked about here
+ // https://rust-lang.github.io/unsafe-code-guidelines/layout/unions.html#c-compatible-layout-repr-c
+ #[repr(C)]
+ union Transmute<T, U> {
+ t: ManuallyDrop<T>,
+ u: ManuallyDrop<U>,
+ }
+
+ // Create the union in the `T` state.
+ let value = Transmute {
+ t: ManuallyDrop::new(value),
+ };
+
+ // Read from the union in the `U` state.
+ // SAFETY: This is safe because the caller has promised that `T` can be transmuted to `U`.
+ // The following reference link talks about repr(C) unions being used this way.
+ // https://doc.rust-lang.org/reference/items/unions.html#reading-and-writing-union-fields
+ ManuallyDrop::into_inner(unsafe { value.u })
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ use sealed::RawIndirect;
+
+ #[test]
+ fn can_type_erase_borrow() {
+ let x = 42;
+ let y: &i32 = &x;
+ let z = RawIndirect::<Ref>::new(y);
+
+ // SAFETY: Same type as y which we made it from.
+ let w: &i32 = unsafe { z.into_inner() };
+
+ assert_eq!(w, y);
+ }
+
+ trait AsInt {
+ fn as_int(&self) -> i32;
+ }
+
+ impl AsInt for i32 {
+ fn as_int(&self) -> i32 {
+ *self
+ }
+ }
+
+ #[test]
+ fn can_type_erase_unsized_borrow() {
+ let x = 42;
+ let y: &dyn AsInt = &x;
+ let z = RawIndirect::<Ref>::new(y);
+
+ // SAFETY: Same type as y which we made it from.
+ let w: &i32 = unsafe { z.into_inner() };
+
+ assert_eq!(w.as_int(), x);
+ }
+
+ #[test]
+ fn can_type_erase_mut_borrow() {
+ let mut x = 42;
+ let y: &mut i32 = &mut x;
+ let z = RawIndirect::<Mut>::new(y);
+
+ // SAFETY: Same type as y which we made it from.
+ let w: &mut i32 = unsafe { z.into_inner() };
+
+ *w += 1;
+ assert_eq!(x, 43);
+ }
+
+ trait AddOne {
+ fn add_one(&mut self);
+ }
+
+ impl AddOne for i32 {
+ fn add_one(&mut self) {
+ *self += 1;
+ }
+ }
+
+ #[test]
+ fn can_type_erase_unsized_mut_borrow() {
+ let mut x = 42;
+ let y: &mut dyn AddOne = &mut x;
+ let z = RawIndirect::<Mut>::new(y);
+
+ // SAFETY: Same type as y which we made it from.
+ let w: &mut dyn AddOne = unsafe { z.into_inner() };
+
+ w.add_one();
+
+ assert_eq!(x, 43);
+ }
+}