devela/code/any/
ext.rs

1// devela::code::any::ext
2//
3//! Defines the [`ExtAny`] trait.
4//
5// - WAIT: (const) [type_name](https://github.com/rust-lang/rust/issues/63084)
6// - WAIT: [trait_upcasting](https://github.com/rust-lang/rust/issues/65991)
7
8use crate::{Any, Hash, Hasher, HasherFx, TypeId};
9use core::any::type_name_of_val;
10
11#[cfg(feature = "alloc")]
12use crate::Box;
13
14/// Marker trait to prevent downstream implementations of the [`ExtAny`] trait.
15trait Sealed {}
16impl<T: ?Sized + Any> Sealed for T {}
17impl<T: ?Sized + Any> ExtAny for T {}
18
19#[doc = crate::TAG_NAMESPACE!()]
20/// Extension trait providing convenience methods for `T:`[`Any`].
21///
22/// This trait is sealed and cannot be implemented manually.
23#[rustfmt::skip]
24#[expect(private_bounds, reason = "Sealed")]
25pub trait ExtAny: Any + Sealed {
26
27    /* type */
28
29    /// Returns the `TypeId` of `Self`.
30    ///
31    /// # Example
32    /// ```
33    /// use devela::ExtAny;
34    ///
35    /// let x = 5;
36    /// assert_eq!(x.type_of(), i32::type_id());
37    /// ```
38    #[must_use]
39    fn type_id() -> TypeId { TypeId::of::<Self>() }
40
41    /// Returns the `TypeId` of `self`.
42    ///
43    /// # Example
44    /// ```
45    /// use devela::ExtAny;
46    ///
47    /// let x = 5;
48    /// assert_eq!(x.type_of(), i32::type_id());
49    /// ```
50    #[must_use]
51    fn type_of(&self) -> TypeId { TypeId::of::<Self>() }
52
53    /// Returns the type name of `self`.
54    ///
55    /// # Example
56    /// ```
57    /// use devela::ExtAny;
58    ///
59    /// let x = 5;
60    /// assert_eq!(x.type_name(), "i32");
61    /// ```
62    #[must_use]
63    fn type_name(&self) -> &'static str { type_name_of_val(self) }
64
65    /// Returns `true` if `Self` is of type `T`.
66    ///
67    /// # Example
68    /// ```
69    /// use devela::ExtAny;
70    ///
71    /// let val = 5;
72    /// assert!(val.type_is::<i32>());
73    /// assert!(!val.type_is::<u32>());
74    ///
75    /// // Compared to Any::is():
76    /// let any = val.as_any_ref();
77    /// // assert!(any.type_is::<i32>()); // doesn't work for &dyn Any
78    /// // assert!(val.is::<i32>()); // doesn't work for T: Any
79    /// assert!(any.is::<i32>()); // does work for &dyn Any
80    /// ```
81    #[must_use]
82    fn type_is<T: 'static>(&self) -> bool { self.type_id() == TypeId::of::<T>() }
83
84    /// Returns a deterministic hash of the `TypeId` of `Self`.
85    fn type_hash(&self) -> u64 {
86        let hasher = HasherFx::<u64>::default();
87        self.type_hash_with(hasher)
88    }
89
90    /// Returns a deterministic hash of the `TypeId` of `Self` using a custom hasher.
91    fn type_hash_with<H: Hasher>(&self, mut hasher: H) -> u64 {
92        TypeId::of::<Self>().hash(&mut hasher);
93        hasher.finish()
94    }
95
96    /* upcasts */
97
98    /// Upcasts `&self` as `&dyn Any`.
99    ///
100    /// # Example
101    /// ```
102    /// use devela::{Any, ExtAny};
103    ///
104    /// let val = 5;
105    /// let any: &dyn Any = &val as &dyn Any;
106    /// assert!(any.is::<i32>()); // works direcly for dyn Any
107    /// ```
108    #[must_use]
109    fn as_any_ref(&self) -> &dyn Any where Self: Sized { self }
110
111    /// Upcasts `&mut self` as `&mut dyn Any`.
112    ///
113    /// # Example
114    /// ```
115    /// use devela::{Any, ExtAny};
116    ///
117    /// let mut x = 5;
118    /// let any: &mut dyn Any = x.as_any_mut();
119    /// assert!(any.is::<i32>());
120    /// ```
121    #[must_use]
122    fn as_any_mut(&mut self) -> &mut dyn Any where Self: Sized { self }
123
124    /// Upcasts `Box<self>` as `Box<dyn Any>`.
125    ///
126    /// # Example
127    /// ```
128    /// use devela::{Any, ExtAny};
129    ///
130    /// let x = Box::new(5);
131    /// let any: Box<dyn Any> = x.as_any_box();
132    /// assert!(any.is::<i32>());
133    /// ```
134    #[must_use]
135    #[cfg(feature = "alloc")]
136    fn as_any_box(self: Box<Self>) ->  Box<dyn Any> where Self: Sized { self }
137
138    /* downcasts */
139
140    /// Returns some shared reference to the inner value if it is of type `T`.
141    ///
142    /// This method is only needed when not dealing directly with `dyn Any` trait objects,
143    /// since it's [already implemented for `dyn Any`](Any#method.downcast_ref).
144    ///
145    /// # Example
146    /// ```
147    /// use core::fmt::Display;
148    /// use devela::{Any, ExtAny};
149    ///
150    /// trait Trait: Any + Display {}
151    /// impl Trait for i32 {}
152    /// impl Trait for char {}
153    /// impl Trait for bool {}
154    ///
155    /// # #[cfg(feature = "alloc")]
156    /// // in the heap:
157    /// {
158    ///     # use devela::{Box, Vec};
159    ///     let b: Box<dyn Trait> = Box::new(5);
160    ///     if let Some(n) = (*b).downcast_ref::<i32>() {
161    ///         assert_eq![n, &5_i32];
162    ///     }
163    ///
164    ///     let bb: Vec<Box<dyn Trait>> = vec![Box::new(true), Box::new(6), Box::new('c')];
165    ///     for b in bb {
166    ///         if let Some(n) = (*b).downcast_ref::<i32>() {
167    ///             assert_eq![n, &6_i32];
168    ///         }
169    ///     }
170    /// }
171    /// // in the stack:
172    /// # #[cfg_attr(not(feature = "__force_miri_dst"), cfg(not(miri)))] // FIX
173    /// {
174    ///     use devela::{Any, DstArray, DstStack, DstValue, ExtAny};
175    ///     let v = DstValue::<dyn Trait, DstArray<usize, 2>>::new(7, |v| v as _).unwrap();
176    ///     if let Some(n) = (*v).downcast_ref::<i32>() {
177    ///         assert_eq![n, &7_i32];
178    ///     }
179    ///
180    ///     let mut vv = DstStack::<dyn Trait, DstArray<u32, 12>>::new();
181    ///     vv.push(true, |v| v).unwrap();
182    ///     vv.push(8_i32, |v| v).unwrap();
183    ///     vv.push('c', |v| v).unwrap();
184    ///     for v in vv.iter() {
185    ///         if let Some(n) = (*v).downcast_ref::<i32>() {
186    ///             assert_eq![n, &8_i32];
187    ///         }
188    ///     }
189    /// }
190    /// ```
191    #[must_use]
192    #[cfg(feature = "unsafe_layout")]
193    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "unsafe_layout")))]
194    fn downcast_ref<T: 'static>(&self) -> Option<&T> {
195        // SAFETY: We verify T is of the right type before downcasting
196        unsafe { (*self).type_is::<T>().then(|| &*<*const _>::cast(self)) }
197    }
198
199    /// Returns some exclusive reference to the inner value if it is of type `T`.
200    ///
201    /// This method is only needed when not dealing directly with `dyn Any` trait objects,
202    /// since it's [already implemented for `dyn Any`][Any#method.downcast_mut].
203    #[must_use]
204    #[cfg(feature = "unsafe_layout")]
205    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "unsafe_layout")))]
206    fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
207        // SAFETY: We verify T is of the right type before downcasting
208        unsafe { (*self).type_is::<T>().then(|| &mut *<*mut _>::cast(self)) }
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use crate::ExtAny;
215
216    #[test]
217    fn closure_type_ids() {
218        let closure1 = || {};
219        let closure2 = || {};
220        let closure_with_env = |x: i32| x + 1;
221
222        // Ensure `type_of` produces unique `TypeId`s for each closure.
223        assert_ne!(closure1.type_of(), closure2.type_of());
224        assert_ne!(closure1.type_of(), closure_with_env.type_of());
225        assert_ne!(closure2.type_of(), closure_with_env.type_of());
226
227        // All closure names in the same module are the same.
228        assert_eq!(closure1.type_name(), closure2.type_name());
229        assert_eq!(closure1.type_name(), closure_with_env.type_name());
230    }
231}