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}