use crate::hkt::ImpliedBound;
use super::{erased::Erased, type_name, WithLtTypeId};
/// Type erased mutable borrow of a unsized type.
///
/// This type acts like a `&'a mut dyn Trait<'ctx> + 'lt` with the `Trait` part erased.
/// This type exists to get a mutable borrow of an arbitrary trait object out of a object safe method.
/// However, it is not limited to storing mutable borrows of trait objects. It can be
/// used to store a mutable borrow to any type including sized ones.
pub struct MutAnyUnsized<'r, 'lt> {
/// As shown in the table at https://doc.rust-lang.org/nomicon/subtyping.html#variance
/// mutable borrows are covariant over their lifetime, so we are also.
_b: ImpliedBound<'r, 'lt>,
/// 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::<&mut dyn core::any::Any>() }>,
/// 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>,
}
impl<'r, 'lt> MutAnyUnsized<'r, 'lt> {
/// Create from a borrow.
pub fn new<T: ?Sized + type_name::WithLt<'r, 'lt>>(borrow: &'r mut T) -> Self {
Self {
_b: ImpliedBound::NEW,
borrow: Erased::new::<&'r mut 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<'r, 'lt>>(self) -> Result<&'r mut T, Self> {
if (self.type_name_id)() == WithLtTypeId::of::<T>() {
#[allow(unsafe_code)]
Ok(unsafe { self.borrow.into_inner::<&'r mut T>() })
} else {
Err(self)
}
}
}