//! Heapless type erasure.
//!
//! [`Any`] is generic over the type of indirection (`&T`, `&mut T`, `Box<T>`)
//! and allows erasing the `T` of the indirection. This is similar to replacing the `T` with
//! [`core::any::Any`]. The main difference to [`core::any::Any`] is
use core::marker::PhantomData;
pub trait TypeNameable<'lt> {
type Name: TypeName<'lt, Nameable = Self>;
}
pub trait TypeName<'lt>: 'static {
type Nameable: ?Sized + TypeNameable<'lt, Name = Self>;
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
pub struct LtTypeId<'lt> {
_marker: PhantomData<fn(&'lt ()) -> &'lt ()>,
name_id: core::any::TypeId,
}
impl<'lt> LtTypeId<'lt> {
pub fn of<T: ?Sized + TypeNameable<'lt>>() -> Self {
LtTypeId {
_marker: PhantomData,
name_id: core::any::TypeId::of::<T::Name>(),
}
}
}
pub unsafe trait LtAny<'lt> {
fn type_id(&self) -> LtTypeId<'lt>;
}
unsafe impl<'lt, T: ?Sized + TypeNameable<'lt>> LtAny<'lt> for T {
fn type_id(&self) -> LtTypeId<'lt> {
LtTypeId::of::<T>()
}
}
impl<'a, 'lt> dyn LtAny<'lt> + 'a {
pub fn is<T: ?Sized + TypeNameable<'lt>>(&self) -> bool {
LtTypeId::of::<T>() == self.type_id()
}
pub fn downcast_ref<T: ?Sized + TypeNameable<'lt>>(&self) -> Option<&T> {
if self.is::<T>() {
Some(unsafe { &*(self as *const dyn LtAny<'lt> as *const T) })
} else {
None
}
}
pub fn downcast_mut<T: ?Sized + TypeNameable<'lt>>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
Some(unsafe { &mut *(self as *mut dyn LtAny<'lt> as *mut T) })
} else {
None
}
}
#[cfg(feature = "alloc")]
pub fn downcast_box<T: TypeNameable<'lt>>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
if self.is::<T>() {
Ok(unsafe {
let raw: *mut dyn LtAny<'lt> = Box::into_raw(self);
Box::from_raw(raw as *mut T)
})
} else {
Err(self)
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! nameable {
{$lt:lifetime: $type:ty => $name:ty} => {
impl<$lt> $crate::any::TypeNameable<$lt> for $type {
type Name = $name;
}
impl<$lt> TypeName<$lt> for $name {
type Nameable = $type;
}
}
}
#[doc(inline)]
pub use nameable;
#[cfg(test)]
mod test {
use super::*;
#[derive(Debug, PartialEq)]
struct X<'a>(&'a mut i32);
nameable!('a: X<'a> => X<'static>);
#[test]
fn any() {
let mut x = 42;
let x = X(&mut x);
let any: &dyn LtAny = &x;
let y = any.downcast_ref().unwrap();
assert_eq!(x, *y);
}
#[test]
#[cfg(feature = "alloc")]
fn any_box() {
let mut x = 42;
let x = X(&mut x);
let any: Box<dyn LtAny> = Box::new(x);
let Ok(y) = any.downcast_box::<X>() else { panic!() };
assert_eq!(*y.0, 42);
}
#[test]
fn equal() {
let mut x = 42;
let x = X(&mut x);
let any: &dyn LtAny = &x;
{
let mut x2 = 42;
let x2 = X(&mut x2);
let any2: &dyn LtAny = &x2;
assert_eq!(any.type_id(), any2.type_id());
}
// Forcing any to live until here will make it fail.
// drop(any);
}
}