1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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)
        }
    }
}