multi_trait_object/
lib.rs

1#![doc=include_str!("../README.md")]
2#[cfg(test)]
3mod tests;
4
5pub(crate) mod macros;
6mod trait_impl;
7mod std_impl;
8
9pub use trait_impl::*;
10pub use std_impl::*;
11
12use std::any::{Any, TypeId};
13use std::collections::HashMap;
14
15#[doc(hidden)]
16#[derive(Copy, Clone, Debug)]
17#[repr(C)]
18pub struct FatPointer {
19    pub data: *const (),
20    pub vptr: *const (),
21}
22
23#[allow(unused)]
24#[doc(hidden)]
25#[inline]
26pub unsafe fn null_ptr<T: Sized>(_value: &T) -> *const T {
27    // creates a typed clone that is actually null
28    std::ptr::null::<T>() as *const T
29}
30
31/// A container to store data with the associated type and trait objects
32/// allowing for casting down to trait_impl or the concrete type
33/// ```rust
34/// use multi_trait_object::*;
35/// use std::fmt::{Debug, Display};
36///
37/// let mto = create_object!(String::new(), dyn Debug, dyn Display);
38///
39/// let debug = mto.downcast_trait::<dyn Debug>().unwrap();
40/// println!("{:?}", debug);
41/// let display = mto.downcast_trait::<dyn Display>().unwrap();
42/// println!("{}", display);
43/// let string = mto.downcast::<String>().unwrap();
44/// println!("{}", string);
45/// ```
46pub struct MultitraitObject {
47    pub(crate) data: *mut (),
48    pub(crate) original_typeid: TypeId,
49    pub(crate) traits: HashMap<TypeId, *const ()>,
50}
51
52impl MultitraitObject {
53    /// Creates a new multitrait object from the given value
54    /// All trait_impl except Any must be registered on this object
55    /// in order to access them.
56    #[doc(hidden)]
57    pub fn new<T: 'static + Any>(value: T) -> Self {
58        let any_vtable = __fat_pointer!(T as dyn Any).vptr;
59        let data = Box::into_raw(Box::new(value)) as *mut ();
60
61        let mut this = Self {
62            data,
63            original_typeid: TypeId::of::<T>(),
64            traits: Default::default(),
65        };
66        this._register::<dyn Any>(any_vtable);
67
68        this
69    }
70
71    /// Downcasts the object into a reference of the given trait
72    pub fn downcast_trait<T1: 'static + ?Sized>(&self) -> Option<&T1> {
73        if std::mem::size_of::<&T1>() != std::mem::size_of::<FatPointer>() {
74            None
75        } else {
76            unsafe {
77                self._downcast_trait::<T1>()
78            }
79        }
80    }
81
82    /// Downcasts the object into a mutable reference of the given trait
83    pub fn downcast_trait_mut<T1: 'static + ?Sized>(&mut self) -> Option<&mut T1> {
84        if std::mem::size_of::<&T1>() != std::mem::size_of::<FatPointer>() {
85            None
86        } else {
87            unsafe {
88                self._downcast_trait_mut::<T1>()
89            }
90        }
91    }
92
93    /// Downcasts the object to a boxed representation of the given trait
94    pub fn downcast_trait_boxed<T1: 'static + ?Sized>(mut self) -> Option<Box<T1>> {
95        if std::mem::size_of::<&T1>() != std::mem::size_of::<FatPointer>() {
96            None
97        } else {
98            unsafe {
99                self._downcast_boxed_trait::<T1>()
100            }
101        }
102    }
103
104    /// Downcasts the object into a reference of the given type
105    #[inline]
106    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
107        let any = self.downcast_trait::<dyn Any>().unwrap();
108        any.downcast_ref::<T>()
109    }
110
111    /// Downcasts the object into a mutable reference of the given type
112    #[inline]
113    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
114        let any = self.downcast_trait_mut::<dyn Any>().unwrap();
115        any.downcast_mut::<T>()
116    }
117
118    /// Downcasts the object into the given concrete type
119    pub fn downcast<T: Any>(self) -> Option<T> {
120        if TypeId::of::<T>() == self.original_typeid {
121            unsafe {
122                // SAFETY: We've checked for the type so it's safe to cast the data and consume it in the process
123                let typed_ptr = std::mem::transmute::<_, *mut T>(self.data);
124                let boxed = Box::from_raw(typed_ptr);
125
126                Some(*boxed)
127            }
128        } else {
129            None
130        }
131    }
132
133    /// Returns if the object stores the given concrete type
134    /// Use [MultitraitObject::implements] to check if the object
135    /// has a given trait
136    #[inline]
137    pub fn is<T: Any>(&self) -> bool {
138        self.original_typeid == TypeId::of::<T>()
139    }
140
141    /// Returns if the object implements a given trait
142    #[inline]
143    pub fn implements<T: 'static + ?Sized>(&self) -> bool {
144        self.traits.contains_key(&TypeId::of::<T>())
145    }
146
147    #[doc(hidden)]
148    #[inline]
149    pub fn _register<T2: 'static + ?Sized>(&mut self, vtable_ptr: *const ()) {
150        self.traits.insert(TypeId::of::<T2>(), vtable_ptr);
151    }
152
153    #[doc(hidden)]
154    #[inline]
155    unsafe fn _downcast_trait<T1: 'static + ?Sized>(&self) -> Option<&T1> {
156        // SAFETY: Creating a fat pointer from the given v-table and data has no side effects
157        let vptr = *self.traits.get(&TypeId::of::<T1>())?;
158        let fat_pointer = FatPointer { data: self.data, vptr };
159        let value = std::mem::transmute::<_, &&T1>(&fat_pointer);
160
161        Some(*value)
162    }
163
164    #[doc(hidden)]
165    #[inline]
166    unsafe fn _downcast_trait_mut<T1: 'static + ?Sized>(&mut self) -> Option<&mut T1> {
167        // SAFETY: Creating a fat pointer from the given v-table and data has no side effects
168        let vptr = *self.traits.get(&TypeId::of::<T1>())?;
169        let mut fat_pointer = FatPointer { data: self.data, vptr };
170        let value = std::mem::transmute::<_, &mut &mut T1>(&mut fat_pointer);
171
172        Some(*value)
173    }
174
175    #[doc(hidden)]
176    #[inline]
177    unsafe fn _downcast_boxed_trait<T1: 'static + ?Sized>(&mut self) -> Option<Box<T1>> {
178        // SAFETY: Creating a fat pointer from the given v-table and data has no side effects
179        let vptr = *self.traits.get(&TypeId::of::<T1>())?;
180        let fat_pointer = FatPointer { data: self.data, vptr };
181        let value = std::mem::transmute::<_, *const *mut T1>(&fat_pointer);
182        let value = Box::from_raw(*value);
183
184        Some(value)
185    }
186}
187
188impl Drop for MultitraitObject {
189    fn drop(&mut self) {
190        unsafe {
191            // SAFETY: The Multitrait object has exclusive access to the data pointer
192            let raw = Box::from_raw(self.data);
193            std::mem::drop(raw);
194        }
195    }
196}
197
198pub trait IntoMultitrait {
199    fn into_multitrait(self) -> MultitraitObject;
200}
201
202impl<T> From<T> for MultitraitObject where T: IntoMultitrait {
203    fn from(other: T) -> Self {
204        other.into_multitrait()
205    }
206}