#![doc=include_str!("../README.md")] #[cfg(test)] mod tests; pub(crate) mod macros; mod trait_impl; mod std_impl; pub use trait_impl::*; pub use std_impl::*; use std::any::{Any, TypeId}; use std::collections::HashMap; #[doc(hidden)] #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct FatPointer { pub data: *const (), pub vptr: *const (), } #[allow(unused)] #[doc(hidden)] #[inline] pub unsafe fn null_ptr(_value: &T) -> *const T { // creates a typed clone that is actually null std::ptr::null::() as *const T } /// A container to store data with the associated type and trait objects /// allowing for casting down to trait_impl or the concrete type /// ```rust /// use multi_trait_object::*; /// use std::fmt::{Debug, Display}; /// /// let mto = create_object!(String::new(), dyn Debug, dyn Display); /// /// let debug = mto.downcast_trait::().unwrap(); /// println!("{:?}", debug); /// let display = mto.downcast_trait::().unwrap(); /// println!("{}", display); /// let string = mto.downcast::().unwrap(); /// println!("{}", string); /// ``` pub struct MultitraitObject { pub(crate) data: *mut (), pub(crate) original_typeid: TypeId, pub(crate) traits: HashMap, } impl MultitraitObject { /// Creates a new multitrait object from the given value /// All trait_impl except Any must be registered on this object /// in order to access them. #[doc(hidden)] pub fn new(value: T) -> Self { let any_vtable = __fat_pointer!(T as dyn Any).vptr; let data = Box::into_raw(Box::new(value)) as *mut (); let mut this = Self { data, original_typeid: TypeId::of::(), traits: Default::default(), }; unsafe { this._register::(any_vtable); } this } /// Downcasts the object into a reference of the given trait pub fn downcast_trait(&self) -> Option<&T1> { if std::mem::size_of::<&T1>() != std::mem::size_of::() { None } else { unsafe { self._downcast_trait::() } } } /// Downcasts the object into a mutable reference of the given trait pub fn downcast_trait_mut(&mut self) -> Option<&mut T1> { if std::mem::size_of::<&T1>() != std::mem::size_of::() { None } else { unsafe { // SAFETY: We've checked the size of the pointer self._downcast_trait_mut::() } } } /// Downcasts the object to a boxed representation of the given trait pub fn downcast_trait_boxed(mut self) -> Option> { if std::mem::size_of::<&T1>() != std::mem::size_of::() { None } else { unsafe { // SAFETY: We've checked the size of the pointer self._downcast_boxed_trait::() } } } /// Downcasts the object into a reference of the given type #[inline] pub fn downcast_ref(&self) -> Option<&T> { let any = self.downcast_trait::().unwrap(); any.downcast_ref::() } /// Downcasts the object into a mutable reference of the given type #[inline] pub fn downcast_mut(&mut self) -> Option<&mut T> { let any = self.downcast_trait_mut::().unwrap(); any.downcast_mut::() } /// Downcasts the object into the given concrete type pub fn downcast(self) -> Option { if TypeId::of::() == self.original_typeid { unsafe { // SAFETY: We've checked for the type so it's safe to cast the data and consume it in the process let typed_ptr = std::mem::transmute::<_, *mut T>(self.data); let boxed = Box::from_raw(typed_ptr); Some(*boxed) } } else { None } } /// Returns if the object stores the given concrete type /// Use [MultitraitObject::implements] to check if the object /// has a given trait #[inline] pub fn is(&self) -> bool { self.original_typeid == TypeId::of::() } /// Returns if the object implements a given trait #[inline] pub fn implements(&self) -> bool { self.traits.contains_key(&TypeId::of::()) } #[doc(hidden)] #[inline] pub unsafe fn _register(&mut self, vtable_ptr: *const ()) { // this function is considers unsafe as there is no validation of the given raw pointer self.traits.insert(TypeId::of::(), vtable_ptr); } #[doc(hidden)] #[inline] unsafe fn _downcast_trait(&self) -> Option<&T1> { let vptr = *self.traits.get(&TypeId::of::())?; let fat_pointer = FatPointer { data: self.data, vptr }; let value = std::mem::transmute::<_, &&T1>(&fat_pointer); Some(*value) } #[doc(hidden)] #[inline] unsafe fn _downcast_trait_mut(&mut self) -> Option<&mut T1> { let vptr = *self.traits.get(&TypeId::of::())?; let mut fat_pointer = FatPointer { data: self.data, vptr }; let value = std::mem::transmute::<_, &mut &mut T1>(&mut fat_pointer); Some(*value) } #[doc(hidden)] #[inline] unsafe fn _downcast_boxed_trait(&mut self) -> Option> { let vptr = *self.traits.get(&TypeId::of::())?; let fat_pointer = FatPointer { data: self.data, vptr }; let value = std::mem::transmute::<_, *const *mut T1>(&fat_pointer); let value = Box::from_raw(*value); Some(value) } } impl Drop for MultitraitObject { fn drop(&mut self) { unsafe { // SAFETY: The Multitrait object has exclusive access to the data pointer let raw = Box::from_raw(self.data); std::mem::drop(raw); } } } pub trait IntoMultitrait { fn into_multitrait(self) -> MultitraitObject; } impl From for MultitraitObject where T: IntoMultitrait { fn from(other: T) -> Self { other.into_multitrait() } }