use crate::{
transience::{CanRecoverFrom, CanTranscendTo, Transience},
transient::Transient,
};
use std::marker::{Send, Sync};
#[rustversion::before(1.76)]
pub use std::any::type_name;
#[rustversion::since(1.76)]
pub use std::any::{type_name, type_name_of_val};
pub trait Any<R: Transience = ()> {
fn type_id(&self) -> TypeId;
}
impl<T: Transient, R: Transience> Any<R> for T
where
T::Transience: CanTranscendTo<R>,
{
#[inline]
fn type_id(&self) -> TypeId {
TypeId::of::<T>()
}
}
pub trait Downcast<R: Transience> {
fn is<T: Transient>(&self) -> bool;
fn downcast<T: Transient>(self: Box<Self>) -> Result<Box<T>, Box<Self>>
where
T::Transience: CanRecoverFrom<R>;
fn downcast_ref<T: Transient>(&self) -> Option<&T>
where
T::Transience: CanRecoverFrom<R>;
fn downcast_mut<T: Transient>(&mut self) -> Option<&mut T>
where
T::Transience: CanRecoverFrom<R>;
unsafe fn downcast_unchecked<T: Transient>(self: Box<Self>) -> Box<T>
where
T::Transience: CanRecoverFrom<R>;
unsafe fn downcast_ref_unchecked<T: Transient>(&self) -> &T
where
T::Transience: CanRecoverFrom<R>;
unsafe fn downcast_mut_unchecked<T: Transient>(&mut self) -> &mut T
where
T::Transience: CanRecoverFrom<R>;
}
macro_rules! dyn_any_impls {
($($for:tt)*) => {
impl<R: Transience> Downcast<R> for dyn Any<R> $($for)* + '_ {
#[inline]
fn is<T: Transient>(&self) -> bool {
self.type_id() == TypeId::of::<T>()
}
#[inline]
fn downcast<T: Transient>(self: Box<Self>) -> Result<Box<T>, Box<Self>>
where
T::Transience: CanRecoverFrom<R>,
{
if <Self as Downcast<R>>::is::<T>(self.as_ref()) {
Ok(unsafe { self.downcast_unchecked() })
} else {
Err(self)
}
}
#[inline]
fn downcast_ref<T: Transient>(&self) -> Option<&T>
where
T::Transience: CanRecoverFrom<R>,
{
if <Self as Downcast<R>>::is::<T>(self) {
Some(unsafe { self.downcast_ref_unchecked() })
} else {
None
}
}
#[inline]
fn downcast_mut<T: Transient>(&mut self) -> Option<&mut T>
where
T::Transience: CanRecoverFrom<R>,
{
if <Self as Downcast<R>>::is::<T>(self) {
Some(unsafe { self.downcast_mut_unchecked() })
} else {
None
}
}
#[inline]
unsafe fn downcast_unchecked<T: Transient>(self: Box<Self>) -> Box<T>
where
T::Transience: CanRecoverFrom<R>,
{
Box::from_raw(Box::into_raw(self).cast())
}
#[inline]
unsafe fn downcast_ref_unchecked<T: Transient>(&self) -> &T
where
T::Transience: CanRecoverFrom<R>,
{
&*(self as *const Self).cast()
}
#[inline]
unsafe fn downcast_mut_unchecked<T: Transient>(&mut self) -> &mut T
where
T::Transience: CanRecoverFrom<R>,
{
&mut *(self as *mut Self).cast()
}
}
impl<R: Transience> std::fmt::Debug for dyn Any<R> $($for)* + '_ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("dyn Any<")?;
f.write_str(type_name::<R>().rsplit("::").next().unwrap())?;
let xtraits = stringify!($($for)*);
if xtraits.len() > 0 {
f.write_str("> ")?;
f.write_str(xtraits)
} else {
f.write_str(">")
}
}
}
};
}
dyn_any_impls!();
dyn_any_impls!(+ Send);
dyn_any_impls!(+ Send + Sync);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TypeId(std::any::TypeId);
impl TypeId {
#[inline]
pub fn of<T: Transient>() -> Self {
let () = T::CHECK;
TypeId(std::any::TypeId::of::<T::Static>())
}
#[inline]
pub fn of_val<T: Transient>(_value: &T) -> Self {
TypeId::of::<T>()
}
}
impl From<std::any::TypeId> for TypeId {
#[inline]
fn from(value: std::any::TypeId) -> Self {
TypeId(value)
}
}
impl From<TypeId> for std::any::TypeId {
#[inline]
fn from(value: TypeId) -> Self {
value.0
}
}
impl PartialEq<std::any::TypeId> for TypeId {
#[inline]
fn eq(&self, other: &std::any::TypeId) -> bool {
self.0.eq(other)
}
}
impl std::fmt::Debug for TypeId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::hash::Hash for TypeId {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
#[cfg(test)]
mod tests {
use super::{Any, Downcast};
use crate::{tr::Transient, Co, Inv};
#[test]
fn test_primative() {
let val = 5;
let valref = &val;
let valrefref = &valref;
let erased: Vec<&dyn Any<Co>> = vec![&5, &valref, &valrefref];
assert_eq!(erased[0].downcast_ref::<i32>().unwrap(), &5);
assert_eq!(erased[1].downcast_ref::<&i32>().unwrap(), &valref);
assert_eq!(erased[2].downcast_ref::<&&i32>().unwrap(), &valrefref);
let value = 5_usize;
let ts: Box<dyn Any<()>> = Box::new(5_usize);
let co: Box<dyn Any<Co>> = Box::new(5_usize);
let inv: Box<dyn Any<Inv>> = Box::new(5_usize);
assert_eq!(*ts.downcast::<usize>().unwrap(), value);
assert_eq!(*co.downcast::<usize>().unwrap(), value);
assert_eq!(*inv.downcast::<usize>().unwrap(), value);
let ts: &dyn Any = &value;
let co: &dyn Any<Co> = &value;
let inv: &dyn Any<Inv> = &value;
assert_eq!(ts.downcast_ref::<usize>().unwrap(), &value);
assert_eq!(co.downcast_ref::<usize>().unwrap(), &value);
assert_eq!(inv.downcast_ref::<usize>().unwrap(), &value);
let valref: &usize = &value;
let co: Box<dyn Any<Co>> = Box::new(valref);
let inv: Box<dyn Any<Inv>> = Box::new(valref);
assert_eq!(*co.downcast::<&usize>().unwrap(), valref);
assert_eq!(*inv.downcast::<&usize>().unwrap(), valref);
let co: &dyn Any<Co> = &valref;
let inv: &dyn Any<Inv> = &valref;
assert_eq!(co.downcast_ref::<&usize>().unwrap(), &valref);
assert_eq!(inv.downcast_ref::<&usize>().unwrap(), &valref);
let valrefref = &valref;
let inv_inv: Box<dyn Any<(Inv, Inv)>> = Box::new(valrefref);
let inv_co: Box<dyn Any<(Inv, Co)>> = Box::new(valrefref);
let co_inv: Box<dyn Any<(Co, Inv)>> = Box::new(valrefref);
let co_co: Box<dyn Any<(Co, Co)>> = Box::new(valrefref);
let co: Box<dyn Any<Co>> = Box::new(valrefref);
let inv: Box<dyn Any<Inv>> = Box::new(valrefref);
assert_eq!(*inv_inv.downcast::<&&usize>().unwrap(), valrefref);
assert_eq!(*inv_co.downcast::<&&usize>().unwrap(), valrefref);
assert_eq!(*co_inv.downcast::<&&usize>().unwrap(), valrefref);
assert_eq!(*co_co.downcast::<&&usize>().unwrap(), valrefref);
assert_eq!(*co.downcast::<&&usize>().unwrap(), valrefref);
assert_eq!(*inv.downcast::<&&usize>().unwrap(), valrefref);
let inv_inv: &dyn Any<(Inv, Inv)> = &valrefref;
let co_inv: &dyn Any<(Co, Inv)> = &valrefref;
let inv_co: &dyn Any<(Inv, Co)> = &valrefref;
let co_co: &dyn Any<(Co, Co)> = &valrefref;
let co: &dyn Any<Co> = &valrefref;
let inv: &dyn Any<Inv> = &valrefref;
assert_eq!(inv_inv.downcast_ref::<&&usize>().unwrap(), &valrefref);
assert_eq!(co_inv.downcast_ref::<&&usize>().unwrap(), &valrefref);
assert_eq!(inv_co.downcast_ref::<&&usize>().unwrap(), &valrefref);
assert_eq!(co_co.downcast_ref::<&&usize>().unwrap(), &valrefref);
assert_eq!(co.downcast_ref::<&&usize>().unwrap(), &valrefref);
assert_eq!(inv.downcast_ref::<&&usize>().unwrap(), &valrefref);
{
let mut value = 5_usize;
let mut valmut = &mut value;
let inv_inv: Box<dyn Any<(Inv, Inv)>> = Box::new(&mut valmut);
assert_eq!(
*inv_inv.downcast::<&mut &mut usize>().unwrap(),
&mut &mut 5usize
);
let co_inv: Box<dyn Any<(Co, Inv)>> = Box::new(&mut valmut);
assert_eq!(
*co_inv.downcast::<&mut &mut usize>().unwrap(),
&mut &mut 5usize
);
let inv: Box<dyn Any<Inv>> = Box::new(&mut valmut);
assert_eq!(
*inv.downcast::<&mut &mut usize>().unwrap(),
&mut &mut 5usize
);
}
{
let mut value = 5_usize;
let mut valmut = &mut value;
let valmutmut = &mut valmut;
let inv_inv: &dyn Any<(Inv, Inv)> = &valmutmut;
assert_eq!(
inv_inv.downcast_ref::<&mut &mut usize>().unwrap(),
&&mut &mut 5usize
);
let co_inv: &dyn Any<(Co, Inv)> = &valmutmut;
assert_eq!(
co_inv.downcast_ref::<&mut &mut usize>().unwrap(),
&&mut &mut 5usize
);
let inv: &dyn Any<Inv> = &valmutmut;
assert_eq!(
inv.downcast_ref::<&mut &mut usize>().unwrap(),
&&mut &mut 5usize
);
}
}
#[test]
fn test_custom() {
#[derive(Debug, Clone)]
pub struct Usize(usize);
unsafe impl Transient for Usize {
type Static = Usize;
type Transience = ();
}
#[derive(Debug, Clone, Copy)]
pub struct UsizeRef<'a>(&'a usize);
unsafe impl<'a> Transient for UsizeRef<'a> {
type Static = UsizeRef<'static>;
type Transience = Co<'a>;
}
let usize_ = Usize(5_usize);
let stc: Box<dyn Any<()>> = Box::new(usize_.clone());
let inv: Box<dyn Any<Inv>> = Box::new(usize_.clone());
let co: Box<dyn Any<Co>> = Box::new(usize_.clone());
assert_eq!(stc.downcast::<Usize>().unwrap().0, 5_usize);
assert_eq!(inv.downcast::<Usize>().unwrap().0, 5_usize);
assert_eq!(co.downcast::<Usize>().unwrap().0, 5_usize);
let stc: &dyn Any<()> = &usize_;
let inv: &dyn Any<Inv> = &usize_;
let co: &dyn Any<Co> = &usize_;
assert_eq!(stc.downcast_ref::<Usize>().unwrap().0, 5_usize);
assert_eq!(inv.downcast_ref::<Usize>().unwrap().0, 5_usize);
assert_eq!(co.downcast_ref::<Usize>().unwrap().0, 5_usize);
assert_eq!(&format!("{:?}", stc), "dyn Any<()>");
let usize_ref = UsizeRef(&usize_.0);
let inv: Box<dyn Any<Inv>> = Box::new(usize_ref.clone());
let co: Box<dyn Any<Co>> = Box::new(usize_ref.clone());
assert_eq!(inv.downcast::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(co.downcast::<UsizeRef>().unwrap().0, &5_usize);
let inv: &dyn Any<Inv> = &usize_ref;
let co: &dyn Any<Co> = &usize_ref;
assert_eq!(inv.downcast_ref::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(co.downcast_ref::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(&format!("{:?}", co), "dyn Any<Co>");
let usize_ref = UsizeRef(&usize_.0);
let inv: Box<dyn Any<Inv> + Send> = Box::new(usize_ref.clone());
let co: Box<dyn Any<Co> + Send> = Box::new(usize_ref.clone());
assert_eq!(inv.downcast::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(co.downcast::<UsizeRef>().unwrap().0, &5_usize);
let inv: &(dyn Any<Inv> + Send) = &usize_ref;
let co: &(dyn Any<Co> + Send) = &usize_ref;
assert_eq!(inv.downcast_ref::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(co.downcast_ref::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(&format!("{:?}", co), "dyn Any<Co> + Send");
let usize_ref = UsizeRef(&usize_.0);
let inv: Box<dyn Any<Inv> + Send + Sync> = Box::new(usize_ref.clone());
let co: Box<dyn Any<Co> + Send + Sync> = Box::new(usize_ref.clone());
assert_eq!(inv.downcast::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(co.downcast::<UsizeRef>().unwrap().0, &5_usize);
let inv: &(dyn Any<Inv> + Send + Sync) = &usize_ref;
let co: &(dyn Any<Co> + Send + Sync) = &usize_ref;
assert_eq!(inv.downcast_ref::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(co.downcast_ref::<UsizeRef>().unwrap().0, &5_usize);
assert_eq!(&format!("{:?}", inv), "dyn Any<Inv> + Send + Sync")
}
}