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}