#![doc(html_root_url = "http://docs.rs/const-default/1.0.0")] #![cfg_attr(feature = "unstable-docs", feature(doc_cfg))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "derive")] #[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "derive")))] pub use dyn_any_derive::DynAny; /// Implement this trait for your `dyn Trait` types for all `T: Trait` pub trait UpcastFrom { fn up_from(value: &T) -> &Self; fn up_from_mut(value: &mut T) -> &mut Self; #[cfg(feature = "alloc")] fn up_from_box(value: Box) -> Box; } /// Use this trait to perform your upcasts on dyn traits. Make sure to require it in the supertrait! pub trait Upcast { fn up(&self) -> &U; fn up_mut(&mut self) -> &mut U; #[cfg(feature = "alloc")] fn up_box(self: Box) -> Box; } impl Upcast for T where U: UpcastFrom, { fn up(&self) -> &U { U::up_from(self) } fn up_mut(&mut self) -> &mut U { U::up_from_mut(self) } #[cfg(feature = "alloc")] fn up_box(self: Box) -> Box { U::up_from_box(self) } } use core::any::TypeId; impl<'a, T: DynAny<'a> + 'a> UpcastFrom for dyn DynAny<'a> + 'a { fn up_from(value: &T) -> &(dyn DynAny<'a> + 'a) { value } fn up_from_mut(value: &mut T) -> &mut (dyn DynAny<'a> + 'a) { value } #[cfg(feature = "alloc")] fn up_from_box(value: Box) -> Box { value } } pub trait DynAny<'a> { fn type_id(&self) -> TypeId; #[cfg(feature = "log-bad-types")] fn type_name(&self) -> &'static str; } impl<'a, T: StaticType> DynAny<'a> for T { fn type_id(&self) -> core::any::TypeId { core::any::TypeId::of::() } #[cfg(feature = "log-bad-types")] fn type_name(&self) -> &'static str { core::any::type_name::() } } pub fn downcast_ref<'a, V: StaticType>(i: &'a dyn DynAny<'a>) -> Option<&'a V> { if i.type_id() == core::any::TypeId::of::<::Static>() { // SAFETY: caller guarantees that T is the correct type let ptr = i as *const dyn DynAny<'a> as *const V; Some(unsafe { &*ptr }) } else { None } } #[cfg(feature = "alloc")] pub fn downcast<'a, V: StaticType>(i: Box + 'a>) -> Result, String> { let type_id = DynAny::type_id(i.as_ref()); if type_id == core::any::TypeId::of::<::Static>() { // SAFETY: caller guarantees that T is the correct type let ptr = Box::into_raw(i) as *mut dyn DynAny<'a> as *mut V; Ok(unsafe { Box::from_raw(ptr) }) } else { if type_id == core::any::TypeId::of::<&dyn DynAny<'static>>() { panic!("downcast error: type_id == core::any::TypeId::of::>()"); } #[cfg(feature = "log-bad-types")] { Err(format!("Incorrect type, expected {} but found {}", core::any::type_name::(), DynAny::type_name(i.as_ref()))) } #[cfg(not(feature = "log-bad-types"))] { Err(format!("Incorrect type, expected {}", core::any::type_name::())) } } } pub trait StaticType { type Static: 'static + ?Sized; fn type_id(&self) -> core::any::TypeId { core::any::TypeId::of::() } } pub trait StaticTypeSized { type Static: 'static; fn type_id(&self) -> core::any::TypeId { core::any::TypeId::of::() } } impl StaticTypeSized for T where T::Static: Sized, { type Static = ::Static; } pub trait StaticTypeClone { type Static: 'static + Clone; fn type_id(&self) -> core::any::TypeId { core::any::TypeId::of::() } } impl StaticTypeClone for T where T::Static: Clone, { type Static = ::Static; } macro_rules! impl_type { ($($id:ident$(<$($(($l:lifetime, $s:lifetime)),*|)?$($T:ident),*>)?),*) => { $( impl< $($($T: $crate::StaticTypeSized ,)*)?> $crate::StaticType for $id $(<$($($l,)*)?$($T, )*>)?{ type Static = $id$(<$($($s,)*)?$(<$T as $crate::StaticTypeSized>::Static,)*>)?; } )* }; } #[cfg(feature = "alloc")] impl<'a, T: StaticTypeClone + Clone> StaticType for Cow<'a, T> { type Static = Cow<'static, T::Static>; } impl StaticType for *const [T] { type Static = *const [::Static]; } impl StaticType for *mut [T] { type Static = *mut [::Static]; } impl<'a, T: StaticTypeSized> StaticType for &'a [T] { type Static = &'static [::Static]; } impl<'a> StaticType for &'a str { type Static = &'static str; } impl StaticType for () { type Static = (); } impl<'a, T: 'a + StaticType + ?Sized> StaticType for &'a T { type Static = &'static ::Static; } impl StaticType for [T; N] { type Static = [::Static; N]; } impl<'a> StaticType for dyn DynAny<'a> + '_ { type Static = dyn DynAny<'static>; } #[cfg(feature = "alloc")] pub trait IntoDynAny<'n>: Sized + StaticType + 'n { fn into_dyn(self) -> Box + 'n> { Box::new(self) } } #[cfg(feature = "alloc")] impl<'n, T: StaticType + 'n> IntoDynAny<'n> for T {} #[cfg(feature = "alloc")] impl From<()> for Box> { fn from(_: ()) -> Box> { Box::new(()) } } #[cfg(feature = "alloc")] use alloc::{ borrow::Cow, boxed::Box, collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}, string::String, vec::Vec, }; use core::sync::atomic::*; use core::{ cell::{Cell, RefCell, UnsafeCell}, iter::Empty, marker::{PhantomData, PhantomPinned}, mem::{ManuallyDrop, MaybeUninit}, num::Wrapping, time::Duration, }; impl_type!( Option, Result, Cell, UnsafeCell, RefCell, MaybeUninit, ManuallyDrop, PhantomData, PhantomPinned, Empty, Wrapping, Duration, bool, f32, f64, char, u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, usize, AtomicUsize, i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, isize, AtomicIsize, i128, u128, AtomicBool, AtomicPtr ); #[cfg(feature = "large-atomics")] impl_type!(AtomicU64, AtomicI64); #[cfg(feature = "alloc")] impl_type!( Vec, String, BTreeMap,BTreeSet, LinkedList, VecDeque, BinaryHeap ); #[cfg(feature = "std")] use std::sync::*; #[cfg(feature = "std")] impl_type!(Once, Mutex, RwLock); #[cfg(feature = "rc")] use std::rc::Rc; #[cfg(feature = "rc")] impl_type!(Rc); #[cfg(all(feature = "rc", feature = "alloc"))] use std::sync::Arc; #[cfg(all(feature = "rc", feature = "alloc"))] impl_type!(Arc); #[cfg(feature = "glam")] use glam::*; #[cfg(feature = "glam")] #[rustfmt::skip] impl_type!( IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, BVec2, BVec3, BVec4, Vec2, Vec3, Vec3A, Vec4, DVec2, DVec3, DVec4, Mat2, Mat3, Mat3A, Mat4, DMat2, DMat3, DMat4, Quat, Affine2, Affine3A, DAffine2, DAffine3, DQuat ); #[cfg(feature = "alloc")] impl crate::StaticType for Box { type Static = Box<::Static>; } #[test] fn test_tuple_of_boxes() { let tuple = (Box::new(&1 as &dyn DynAny<'static>), Box::new(&2 as &dyn DynAny<'static>)); let dyn_any = &tuple as &dyn DynAny; assert_eq!(&1, downcast_ref(*downcast_ref::<(Box<&dyn DynAny>, Box<&dyn DynAny>)>(dyn_any).unwrap().0).unwrap()); assert_eq!(&2, downcast_ref(*downcast_ref::<(Box<&dyn DynAny>, Box<&dyn DynAny>)>(dyn_any).unwrap().1).unwrap()); } macro_rules! impl_tuple { (@rec $t:ident) => { }; (@rec $_:ident $($t:ident)+) => { impl_tuple! { @impl $($t)* } impl_tuple! { @rec $($t)* } }; (@impl $($t:ident)*) => { impl< $($t: StaticTypeSized,)*> StaticType for ($($t,)*) { type Static = ($(<$t as $crate::StaticTypeSized>::Static,)*); } }; ($($t:ident)*) => { impl_tuple! { @rec _t $($t)* } }; } impl_tuple! { A B C D E F G H I J K L } #[test] fn simple_downcast() { let x = Box::new(3_u32) as Box; assert_eq!(*downcast::(x).unwrap(), 3_u32); } #[test] #[should_panic] fn simple_downcast_panic() { let x = Box::new(3_i32) as Box; assert_eq!(*downcast::(x).expect("attempted to perform invalid downcast"), 3_u32); }