multi_any/multi_any.rs
1use crate::Meta;
2use crate::TypedMetadata;
3use crate::ptr_metadata::*;
4use std::any::Any;
5use std::any::TypeId;
6
7/// A trait for dynamic typing that allows downcasting to both concrete types and trait objects.
8///
9/// Similar to [`std::any::Any`], but supports downcasting to traits implemented by the type.
10///
11/// Most users should implement this trait via the `#[derive(MultiAny)]` macro.
12///
13/// # Examples
14///
15/// ```
16/// use multi_any::*;
17///
18/// // Traits must be annotated with `#[multi_any]`
19/// // With "nightly" feature this is not required
20/// #[multi_any]
21/// trait Trait1 {}
22/// #[multi_any]
23/// trait Trait2 {}
24///
25/// // MultiAny can be derived, and implemented traits must be specified
26/// #[derive(MultiAny)]
27/// #[multi_any(Trait1, Trait2)]
28/// struct Foo;
29///
30/// impl Trait1 for Foo {}
31/// impl Trait2 for Foo {}
32///
33/// // convert Box<Foo> to Box<dyn MultiAny>
34/// let foo: Box<dyn MultiAny> = Box::new(Foo);
35///
36/// // downcast to concrete type
37/// let foo_ref: &Foo = foo.downcast_ref().unwrap();
38///
39/// // downcast to trait object
40/// let trait_ref = foo.downcast_ref::<dyn Trait1>().unwrap();
41///
42/// // downcast to Box
43/// let foo: Box<dyn Trait1> = foo.downcast().unwrap();
44/// ```
45pub trait MultiAny: Any {
46 /// Returns type-erased metadata for the requested type.
47 ///
48 /// # Parameters
49 /// - `type_id`: The `TypeId` of the requested type or trait object.
50 ///
51 /// # Returns
52 /// - `Some(Meta)` if the type matches the concrete type or a trait implemented by the object.
53 /// - `None` if the type does not match.
54 ///
55 /// Implementors must ensure that the returned `Meta` matches the actual data type and metadata type.
56 fn get_metadata(&self, type_id: TypeId) -> Option<Meta>;
57}
58
59impl dyn MultiAny {
60 /// Attempts to downcast a `MultiAny` object to a reference of type `RequestedType`.
61 ///
62 /// Returns `Some(&RequestedType)` if the object can be interpreted as the requested type,
63 /// or `None` otherwise.
64 ///
65 /// # Type Parameters
66 /// - `RequestedType`: The concrete type or trait object to downcast to.
67 ///
68 /// # Panics
69 /// - Can panic with invalid implementation of `get_metadata` (Derived implementation is guaranteed to be valid.)
70 pub fn downcast_ref<RequestedType>(&self) -> Option<&RequestedType>
71 where
72 RequestedType: Pointee + ?Sized + 'static,
73 RequestedType::Metadata: TypedMetadata,
74 {
75 let meta = self.get_metadata(TypeId::of::<RequestedType>())?;
76 assert_eq!(meta.data_id, self.type_id(), "Wrong Data type");
77
78 let typed_meta = RequestedType::Metadata::from_meta(meta);
79 let data_pointer = self as *const dyn MultiAny as *const ();
80
81 // SAFETY: We are merging raw data pointer with metadata.
82 // This is safe because we know that data type matches the type this metadata was created for.
83 let ptr: *const RequestedType = from_raw_parts(data_pointer, typed_meta);
84
85 // SAFETY: We are converting raw pointer to reference.
86 // This is safe because pointer points to the same data as self.
87 unsafe { &*ptr }.into()
88 }
89
90 /// Attempts to downcast a `MultiAny` object to a mutable reference of type `RequestedType`.
91 ///
92 /// Returns `Some(&mut RequestedType)` if the object can be interpreted as the requested type,
93 /// or `None` otherwise.
94 ///
95 /// # Type Parameters
96 /// - `RequestedType`: The concrete type or trait object to downcast to.
97 ///
98 /// # Panics
99 /// - Can panic with invalid implementation of `get_metadata` (Derived implementation is guaranteed to be valid.)
100 pub fn downcast_mut<RequestedType>(&mut self) -> Option<&mut RequestedType>
101 where
102 RequestedType: Pointee + ?Sized + 'static,
103 RequestedType::Metadata: TypedMetadata,
104 {
105 let ptr = self.downcast_mut_ptr::<RequestedType>()?;
106
107 // SAFETY: We are converting raw pointer to mutable reference.
108 // This is safe because pointer points to the same data as self.
109 unsafe { &mut *ptr }.into()
110 }
111
112 /// Attempts to downcast a `Box<dyn MultiAny>` to a `Box<RequestedType>`.
113 ///
114 /// Returns `Ok(Box<RequestedType>)` if the object can be interpreted as the requested type,
115 /// or `Err(Box<dyn MultiAny>)` otherwise. This is a consuming operation.
116 ///
117 /// # Type Parameters
118 /// - `RequestedType`: The concrete type or trait object to downcast to.
119 ///
120 /// # Panics
121 /// - Can panic with invalid implementation of `get_metadata` (Derived implementation is guaranteed to be valid.)
122 pub fn downcast<RequestedType>(mut self: Box<Self>) -> Result<Box<RequestedType>, Box<Self>>
123 where
124 RequestedType: Pointee + ?Sized + 'static,
125 RequestedType::Metadata: TypedMetadata,
126 {
127 let Some(ptr) = self.downcast_mut_ptr::<RequestedType>() else {
128 return Err(self);
129 };
130
131 // We already have the pointer to data. Leak the box to avoid drop.
132 Box::leak(self);
133
134 // SAFETY: We are creating a box from the raw pointer.
135 // This is safe because the pointer points to data allocated by Box with the same data type, but with different metadata.
136 Ok(unsafe { Box::from_raw(ptr) })
137 }
138
139 fn downcast_mut_ptr<RequestedType>(&mut self) -> Option<*mut RequestedType>
140 where
141 RequestedType: Pointee + ?Sized + 'static,
142 RequestedType::Metadata: TypedMetadata,
143 {
144 let meta = self.get_metadata(TypeId::of::<RequestedType>())?;
145 assert_eq!(meta.data_id, (*self).type_id(), "Wrong Data type");
146
147 let typed_meta = RequestedType::Metadata::from_meta(meta);
148 let data_pointer = self as *mut dyn MultiAny as *mut ();
149
150 // SAFETY: We are merging raw data pointer with metadata.
151 // This is safe because we know that data type matches the type this metadata was created for.
152 let ptr: *mut RequestedType = from_raw_parts_mut(data_pointer, typed_meta);
153
154 Some(ptr)
155 }
156}
157
158impl std::fmt::Debug for dyn MultiAny {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 f.debug_struct("MultiAny").finish()
161 }
162}