Diffstat (limited to 'src/any/ref_any_unsized.rs')
| -rw-r--r-- | src/any/ref_any_unsized.rs | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/any/ref_any_unsized.rs b/src/any/ref_any_unsized.rs new file mode 100644 index 0000000..0772e42 --- /dev/null +++ b/src/any/ref_any_unsized.rs @@ -0,0 +1,50 @@ +use crate::hkt::CovariantLt; + +use super::{erased::Erased, type_name, WithLtTypeId}; + +/// Type erased borrow of a unsized type. +/// +/// This type acts like a `&'a dyn Trait<'ctx> + 'lt` with the `Trait` part erased. +/// This type exists to get a borrow of an arbitrary trait object out of a object safe method. +/// However, it is not limited to storing borrows of trait objects. It can be +/// used to store a borrow to any type including sized ones. +pub struct RefAnyUnsized<'a, 'lt, 'ctx> { + /// As shown in the table at https://doc.rust-lang.org/nomicon/subtyping.html#variance + /// borrows are covariant over their lifetime, so we are also. + _a: CovariantLt<'a>, + + /// The type erased borrow. + /// + /// This should be able to store any unsized type in current Rust as all of + /// them are 2 usize in width. + borrow: Erased<{ core::mem::size_of::<&dyn core::any::Any>() }>, + + /// We would prefer to be covariant over 'lt and 'ctx to match borrows, + /// but this would break the safety requirements of transmuting the type. + /// So instead they are invariant as given by WithLtTypeId. + /// + /// We use a function here to reduce the size down to one usize. + /// This makes a RefAnyUnsized only 3 usize wide. + type_name_id: fn() -> WithLtTypeId<'lt, 'ctx>, +} + +impl<'a, 'lt: 'a, 'ctx: 'lt> RefAnyUnsized<'a, 'lt, 'ctx> { + /// Create from a borrow. + pub fn new<T: ?Sized + type_name::WithLt<'lt, 'ctx>>(borrow: &'a T) -> Self { + Self { + _a: CovariantLt::NEW, + borrow: Erased::new::<&'a T>(borrow), + type_name_id: || WithLtTypeId::of::<T>(), + } + } + + /// Downcast the type erased value back to its original type. + pub fn downcast<T: ?Sized + type_name::WithLt<'lt, 'ctx>>(self) -> Result<&'a T, Self> { + if (self.type_name_id)() == WithLtTypeId::of::<T>() { + #[allow(unsafe_code)] + Ok(unsafe { self.borrow.into_inner::<&'a T>() }) + } else { + Err(self) + } + } +} |