trait_cast/
trait_cast.rs

1use core::{
2  any::{Any, TypeId, type_name},
3  fmt::{self, Debug, Formatter},
4  ptr,
5  ptr::DynMetadata,
6};
7
8#[cfg(feature = "alloc")]
9use alloc::{boxed::Box, rc::Rc, sync::Arc};
10
11/// This trait must be implemented on every concrete type for every trait that `TraitcastableAny`
12/// should be able to downcast to.
13///
14/// This trait is not object save.
15pub trait TraitcastableTo<Target: 'static + ?Sized>: TraitcastableAny {
16  /// The metadata that is required to for the cast
17  const METADATA: DynMetadata<Target>;
18}
19
20/// A struct representing the transformation from `dyn TraitcastableAny` to another `dyn Trait`.
21///
22/// This should generally not be manually used, but generated by the `make_trait_castable` attribute macro.
23pub struct TraitcastTarget {
24  target_type_id: TypeId,
25  target_type_name: &'static str,
26  /// Must point to the `DynMetadata<T>` (where T is the type in `TypeId`)
27  metadata: *const (),
28}
29impl TraitcastTarget {
30  /// Creates a new `TraitcastTarget` from a `TraitcastableTo` implementation.
31  #[must_use]
32  pub const fn from<Src: TraitcastableTo<Target>, Target: 'static + ?Sized>() -> Self {
33    #[allow(clippy::borrow_as_ptr)] // Seems like another false positive
34    Self {
35      target_type_id: TypeId::of::<Target>(),
36      target_type_name: type_name::<Target>(),
37      metadata: ptr::from_ref::<DynMetadata<Target>>(&Src::METADATA).cast::<()>(),
38    }
39  }
40  /// Returns the `TypeId` of the type to which can be cast with this instance.
41  #[must_use]
42  pub const fn target_type_id(&self) -> TypeId {
43    self.target_type_id
44  }
45}
46
47/// A trait marking a type as being potentially able to traitcast from `dyn TraitcastableAny` to another `dyn Trait`.
48/// Use this trait instead of the `Any` trait throughout your program.
49///
50/// This should generally not be manually implemented, but generated by the `make_trait_castable` attribute macro.
51///
52/// # Safety
53/// The function `traitcast_targets` must only produce valid `TraitcastTarget` (That use the metadata associated the the correct source struct).
54/// The function `find_traitcast_target` must not return `Some` unless contained value has the correct target `TypeId`.
55pub unsafe trait TraitcastableAny: Any {
56  /// This function returns a list of all the `TraitcastTarget`'s to which a trait object can be cast, this is then used by the implementations of `TraitcastableAnyInfra` to accomplish the traitcast.
57  /// The function is used to generate debug output for `TraitcastableAny`.
58  /// The default implementation of `find_traitcast_target` uses this function by default.
59  ///
60  /// This should generally not be manually implemented, but generated by the `make_trait_castable` attribute macro.
61  // Note: We dropped `'static` on the return type to better support generics.
62  // > "use of generic parameter from outer function"
63  fn traitcast_targets(&self) -> &[TraitcastTarget];
64
65  /// This function can be implemented to support custom `TypeId` lookup algorithms.
66  /// This may be desired when there are lots of `TraitcastTarget`s (30 or more).
67  ///
68  /// Possible strategies:
69  /// * Unsorted `Vec<TraitcastTarget>` lookup. Hot traits first. - Used by the default implementation.
70  /// * `HashMap`
71  /// * Temporarily removed without replacement: ~`Vec<TraitcastTarget>` sorted by the `TypeId` and performing a binary search on it - Used if feature `const_sort` is used.~
72  fn find_traitcast_target(&self, target: TypeId) -> Option<&TraitcastTarget> {
73    self
74      .traitcast_targets()
75      .iter()
76      .find(|possible| possible.target_type_id == target)
77  }
78
79  /// Returns the `TypeId` of the concrete type.
80  fn type_id(&self) -> TypeId {
81    Any::type_id(self)
82  }
83}
84
85/// Mimics the API of `Any` but additionally allows downcasts to select trait objects.
86// This helper trait was created to be able to use `min_specialization` to generate different implementations for `Sized` and `!Sized` types.
87// The functions actually belong to `TraitcastableAny`.
88pub trait TraitcastableAnyInfra<Target: ?Sized>: 'static {
89  /// Returns true if `Target` is the exact same type as Self.
90  fn is(&self) -> bool;
91
92  /// Returns true if Self can be converted to a `Target`.
93  fn can_be(&self) -> bool;
94
95  /// Returns some reference to the inner value if it is downcastable to `Target`, or `None` if it isn’t.
96  ///
97  /// If `Target` is Sized this is forwarded to `Any::downcast_ref`,
98  /// otherwise `TraitcastableAny::traitcast_targets` is used to determine if a traitcast is possible.
99  ///
100  /// Returns `None` if the concrete type of self is not `Target` and a traitcast is not possible.
101  fn downcast_ref(&self) -> Option<&Target>;
102
103  /// Unchecked variant of `downcast_ref`
104  /// # Safety
105  /// This function is unsafe because the caller must ensure that the cast is valid.
106  #[cfg(feature = "downcast_unchecked")]
107  #[doc(cfg(feature = "downcast_unchecked"))]
108  unsafe fn downcast_ref_unchecked(&self) -> &Target;
109
110  /// Returns some mutable reference to the inner value if it is downcastable to `Target`, or `None` if it isn’t.
111  ///
112  /// If `Target` is Sized this is forwarded to `Any::downcast_ref`,
113  /// otherwise `TraitcastableAny::traitcast_targets` is used to determine if a traitcast is possible.
114  ///
115  /// Returns `None` if the concrete type of self is not `Target` and a traitcast is not possible.
116  fn downcast_mut(&mut self) -> Option<&mut Target>;
117
118  /// Unchecked variant of `downcast_ref`
119  /// # Safety
120  /// This function is unsafe because the caller must ensure that the cast is valid.
121  #[cfg(feature = "downcast_unchecked")]
122  #[doc(cfg(feature = "downcast_unchecked"))]
123  unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target;
124}
125
126// TODO: Allocator api support.
127
128/// Extension Trait to implement over Smart Pointer Types (`Box`, `Rc`, `Arc`).
129///
130/// Tries to mimic the API of `Any` but additionally allows downcasts to select trait objects.
131#[cfg(feature = "alloc")]
132#[doc(cfg(feature = "alloc"))]
133pub trait TraitcastableAnyInfraExt<Target: ?Sized + 'static>: Sized {
134  /// The type that will be returned on a successful cast. Something like `Box<Target>`.
135  type Output;
136
137  /// Same as `downcast_ref` and `downcast_mut`, except that it downcasts a `Box` in place.
138  ///
139  /// Returns `None` if the concrete type of self is not `Target` and a traitcast is not possible.
140  ///
141  /// # Errors
142  /// In case a cast is impossible the original input is returned as the error type.
143  /// Otherwise the box would be dropped.
144  fn downcast(self) -> Result<Self::Output, Self>;
145
146  /// Unchecked variant of `downcast`
147  /// # Safety
148  /// This function is unsafe because the caller must ensure that the cast is valid.
149  #[cfg(feature = "downcast_unchecked")]
150  #[doc(cfg(feature = "downcast_unchecked"))]
151  unsafe fn downcast_unchecked(self) -> Self::Output;
152}
153
154#[cfg(feature = "min_specialization")]
155// Safety:
156// Since `traitcast_targets` returns nothing this is always safe.
157// `find_traitcast_target` has the default implementations.
158unsafe impl<T: 'static> TraitcastableAny for T {
159  default fn traitcast_targets(&self) -> &[TraitcastTarget] {
160    &[]
161  }
162  default fn find_traitcast_target(&self, target: TypeId) -> Option<&TraitcastTarget> {
163    // Note: copied from above
164    self
165      .traitcast_targets()
166      .iter()
167      .find(|possible| possible.target_type_id == target)
168  }
169}
170impl Debug for dyn TraitcastableAny {
171  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172    write!(f, "TraitcastableAny to {{")?;
173    for (i, target) in self.traitcast_targets().iter().enumerate() {
174      if i != 0 {
175        write!(f, ", ")?;
176      }
177      write!(f, "{}", target.target_type_name)?;
178    }
179    write!(f, "}}")
180  }
181}
182macro_rules! implement_with_markers {
183  ($($(+)? $traits:ident)*) => {
184    impl<Target: ?Sized + 'static + $($traits +)*> TraitcastableAnyInfra<Target> for dyn TraitcastableAny $(+ $traits)* {
185      default fn is(&self) -> bool {
186        false
187      }
188      default fn can_be(&self) -> bool {
189        let found_target = self.find_traitcast_target(TypeId::of::<Target>());
190        found_target.is_some()
191      }
192
193      default fn downcast_ref(&self) -> Option<&Target> {
194        // SAFETY:
195        // The invariant of Traitcast target guarantees that the metadata points to an instance of `<Target as ::core::ptr::Pointee>::Metadata`.
196        let metadata = Self::find_traitcast_target(self, TypeId::of::<Target>()).map(|target| unsafe {*(target.metadata.cast::<<Target as ::core::ptr::Pointee>::Metadata>())});
197
198        let raw_ptr = core::ptr::from_ref::<Self>(self).to_raw_parts().0;
199
200        metadata.map(|metadata| {
201          let ret_ptr: *const Target = ptr::from_raw_parts(raw_ptr, metadata);
202
203          // SAFETY:
204          // we turned this into a raw pointer before and changed the metadata to that of a dyn Trait
205          //  where the Trait must be implemented, so this must be safe!
206          unsafe {&*ret_ptr}
207        })
208      }
209      #[cfg(feature = "downcast_unchecked")]
210      default unsafe fn downcast_ref_unchecked(&self) -> &Target {
211        // SAFETY: The caller must ensure that the cast is valid.
212        unsafe { self.downcast_ref().unwrap_unchecked() }
213      }
214
215      default fn downcast_mut(&mut self) -> Option<&mut Target> {
216        // SAFETY:
217        // The invariant of Traitcast target guarantees that the metadata points to an instance of `<Target as ::core::ptr::Pointee>::Metadata`.
218        let metadata = Self::find_traitcast_target(self, TypeId::of::<Target>()).map(|target| unsafe {*(target.metadata.cast::<<Target as ::core::ptr::Pointee>::Metadata>())});
219
220        let raw_ptr = core::ptr::from_mut::<Self>(self).to_raw_parts().0;
221
222        metadata.map(|metadata| {
223          let ret_ptr: *mut Target = ptr::from_raw_parts_mut(raw_ptr, metadata);
224
225          // SAFETY:
226          // we turned this into a raw pointer before and changed the metadata to that of a dyn Trait
227          //  where the Trait must be implemented, so this must be safe!
228          unsafe {&mut *ret_ptr}
229        })
230
231      }
232      #[cfg(feature = "downcast_unchecked")]
233      default unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target {
234        // SAFETY: The caller must ensure that the cast is valid.
235        unsafe { self.downcast_mut().unwrap_unchecked() }
236      }
237    }
238    impl<Target: Sized + 'static + $($traits +)*> TraitcastableAnyInfra<Target> for dyn TraitcastableAny $(+ $traits)* {
239      fn is(&self) -> bool {
240        <dyn Any>::is::<Target>(self)
241      }
242      fn can_be(&self) -> bool {
243        <dyn TraitcastableAny as TraitcastableAnyInfra<Target>>::is(self)
244      }
245      fn downcast_ref(&self) -> Option<&Target> {
246        <dyn Any>::downcast_ref::<Target>(self)
247      }
248      #[cfg(feature = "downcast_unchecked")]
249      unsafe fn downcast_ref_unchecked(&self) -> &Target {
250        // SAFETY: We are just forwarding the call to the `Any` trait.
251        unsafe { <dyn Any>::downcast_ref_unchecked::<Target>(self) }
252      }
253
254      fn downcast_mut(&mut self) -> Option<&mut Target> {
255        <dyn Any>::downcast_mut::<Target>(self)
256      }
257      #[cfg(feature = "downcast_unchecked")]
258      unsafe fn downcast_mut_unchecked(&mut self) -> &mut Target {
259        // SAFETY: We are just forwarding the call to the `Any` trait.
260        unsafe { <dyn Any>::downcast_mut_unchecked::<Target>(self) }
261      }
262    }
263  };
264}
265
266#[cfg(feature = "alloc")]
267impl<Src: TraitcastableAnyInfra<Target> + ?Sized, Target: ?Sized + 'static>
268  TraitcastableAnyInfraExt<Target> for Box<Src>
269{
270  type Output = Box<Target>;
271
272  default fn downcast(self) -> Result<Self::Output, Self> {
273    let raw = Self::into_raw(self);
274    // SAFETY:
275    // We can cast the *mut to a &mut since we never use the pointer directly in the success case
276    //  and the reference isn't passed to the failure case.
277    if let Some(to_ref) = unsafe { &mut *raw }.downcast_mut() {
278      // SAFETY:
279      // The pointer originates from a `Box` with the same dynamic type,
280      //  since we only changed the pointer metadata.
281      Ok(unsafe { Box::from_raw(to_ref) })
282    } else {
283      // SAFETY:
284      // We reconstruct the previously destructed `Box`.
285      Err(unsafe { Self::from_raw(raw) })
286    }
287  }
288  #[cfg(feature = "downcast_unchecked")]
289  default unsafe fn downcast_unchecked(self) -> Self::Output {
290    // SAFETY: The caller must ensure that the cast is valid.
291    unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
292  }
293}
294
295#[cfg(feature = "alloc")]
296impl<Src: TraitcastableAnyInfra<Target>, Target: Sized + 'static> TraitcastableAnyInfraExt<Target>
297  for Box<Src>
298{
299  fn downcast(self) -> Result<Self::Output, Self> {
300    #[cfg(feature = "downcast_unchecked")]
301    if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
302      // SAFETY:
303      // We checked for dynamic type equality `is` in the previous if.
304      unsafe { Ok(<Box<dyn Any>>::downcast_unchecked(self)) }
305    } else {
306      Err(self)
307    }
308    #[cfg(not(feature = "downcast_unchecked"))]
309    if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
310      Ok(<Box<dyn Any>>::downcast::<Target>(self).unwrap())
311    } else {
312      Err(self)
313    }
314  }
315
316  #[cfg(feature = "downcast_unchecked")]
317  unsafe fn downcast_unchecked(self) -> Self::Output {
318    // SAFETY: The caller must ensure that the cast is valid.
319    unsafe { <Box<dyn Any>>::downcast_unchecked::<Target>(self) }
320  }
321}
322
323#[cfg(feature = "alloc")]
324impl<Src: TraitcastableAnyInfra<Target> + ?Sized, Target: ?Sized + 'static>
325  TraitcastableAnyInfraExt<Target> for Rc<Src>
326{
327  type Output = Rc<Target>;
328
329  default fn downcast(self) -> Result<Self::Output, Self> {
330    let raw = Self::into_raw(self);
331    // SAFETY:
332    // We can cast the *mut to a &mut since we never use the pointer directly in the success case
333    //  and the reference isn't passed to the failure case.
334    if let Some(to_ref) = unsafe { &*raw }.downcast_ref() {
335      // SAFETY:
336      // The pointer originates from a `Rc` with the same dynamic type,
337      //  since we only changed the pointer metadata.
338      Ok(unsafe { Rc::from_raw(to_ref) })
339    } else {
340      // SAFETY:
341      // We reconstruct the previously destructed `Rc`.
342      Err(unsafe { Self::from_raw(raw) })
343    }
344  }
345  #[cfg(feature = "downcast_unchecked")]
346  default unsafe fn downcast_unchecked(self) -> Self::Output {
347    // SAFETY: The caller must ensure that the cast is valid.
348    unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
349  }
350}
351
352#[cfg(feature = "alloc")]
353impl<Src: TraitcastableAnyInfra<Target>, Target: Sized + 'static> TraitcastableAnyInfraExt<Target>
354  for Rc<Src>
355{
356  fn downcast(self) -> Result<Self::Output, Self> {
357    #[cfg(feature = "downcast_unchecked")]
358    if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
359      // SAFETY:
360      // We checked for dynamic type equality `is` in the previous if.
361      unsafe { Ok(<Rc<dyn Any>>::downcast_unchecked(self)) }
362    } else {
363      Err(self)
364    }
365    #[cfg(not(feature = "downcast_unchecked"))]
366    if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
367      Ok(<Rc<dyn Any>>::downcast(self).unwrap())
368    } else {
369      Err(self)
370    }
371  }
372
373  #[cfg(feature = "downcast_unchecked")]
374  unsafe fn downcast_unchecked(self) -> Self::Output {
375    // SAFETY: The caller must ensure that the cast is valid.
376    unsafe { <Rc<dyn Any>>::downcast_unchecked::<Target>(self) }
377  }
378}
379
380#[cfg(feature = "alloc")]
381impl<
382  Src: TraitcastableAnyInfra<Target> + ?Sized + Send + Sync,
383  Target: ?Sized + 'static + Send + Sync,
384> TraitcastableAnyInfraExt<Target> for Arc<Src>
385{
386  type Output = Arc<Target>;
387
388  default fn downcast(self) -> Result<Self::Output, Self> {
389    let raw = Self::into_raw(self);
390    // SAFETY:
391    // We can cast the *mut to a &mut since we never use the pointer directly in the success case
392    //  and the reference isn't passed to the failure case.
393    if let Some(to_ref) = unsafe { &*raw }.downcast_ref() {
394      // SAFETY:
395      // The pointer originates from a `Arc` with the same dynamic type,
396      //  since we only changed the pointer metadata.
397      Ok(unsafe { Arc::from_raw(to_ref) })
398    } else {
399      // SAFETY:
400      // We reconstruct the previously destructed `Arc`.
401      Err(unsafe { Self::from_raw(raw) })
402    }
403  }
404  #[cfg(feature = "downcast_unchecked")]
405  default unsafe fn downcast_unchecked(self) -> Self::Output {
406    // SAFETY: The caller must ensure that the cast is valid.
407    unsafe { <Self as TraitcastableAnyInfraExt<Target>>::downcast(self).unwrap_unchecked() }
408  }
409}
410
411#[cfg(feature = "alloc")]
412impl<Src: TraitcastableAnyInfra<Target> + Send + Sync, Target: Sized + 'static + Send + Sync>
413  TraitcastableAnyInfraExt<Target> for Arc<Src>
414{
415  fn downcast(self) -> Result<Self::Output, Self> {
416    #[cfg(feature = "downcast_unchecked")]
417    if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
418      // SAFETY:
419      // We checked for dynamic type equality `is` in the previous if.
420      unsafe { Ok(<Arc<dyn Any + Send + Sync>>::downcast_unchecked(self)) }
421    } else {
422      Err(self)
423    }
424    #[cfg(not(feature = "downcast_unchecked"))]
425    if TraitcastableAnyInfra::<Target>::is(self.as_ref()) {
426      Ok(<Arc<dyn Any + Send + Sync>>::downcast(self).unwrap())
427    } else {
428      Err(self)
429    }
430  }
431
432  #[cfg(feature = "downcast_unchecked")]
433  unsafe fn downcast_unchecked(self) -> Self::Output {
434    // SAFETY: The caller must ensure that the cast is valid.
435    unsafe { <Arc<dyn Any + Send + Sync>>::downcast_unchecked::<Target>(self) }
436  }
437}
438
439implement_with_markers!();
440implement_with_markers!(Send);
441implement_with_markers!(Send + Sync);