manual_generic_struct/
manual_generic_struct.rs

1//! This example demonstrates how to manually implement the `TraitcastableAny` trait for a generic struct `HybridPet`.
2#![allow(clippy::undocumented_unsafe_blocks, clippy::use_self)]
3#![expect(
4  unsafe_code,
5  reason = "Manual traitcast implementations require unsafe code."
6)]
7#![cfg_attr(feature = "min_specialization", feature(min_specialization))]
8#![feature(ptr_metadata)]
9
10use core::{any::type_name, fmt::Display};
11
12use trait_cast::{TraitcastTarget, TraitcastableAny, TraitcastableAnyInfra, TraitcastableTo};
13
14struct HybridPet<T: Display> {
15  name: T,
16}
17impl<T: Display> HybridPet<T> {
18  fn greet(&self) {
19    println!("{}: Hi {}", self.name, type_name::<T>());
20  }
21}
22
23impl<T: Display> Dog for HybridPet<T> {
24  fn bark(&self) {
25    println!("{}: Woof!", self.name);
26  }
27}
28impl<V: Display + ?Sized, T: Display> Cat<V> for HybridPet<T> {
29  fn meow(&self, speak: &V) {
30    println!("{}: Meow! {speak}", self.name);
31  }
32}
33
34trait Dog {
35  fn bark(&self);
36}
37
38trait Cat<T: Display + ?Sized> {
39  fn meow(&self, speak: &T);
40}
41
42impl<T: Display + 'static> TraitcastableTo<dyn Dog> for HybridPet<T> {
43  const METADATA: ::core::ptr::DynMetadata<dyn Dog> = {
44    let self_ptr: *const HybridPet<T> = ::core::ptr::null::<HybridPet<T>>();
45    let dyn_ptr: *const dyn Dog = self_ptr as _;
46
47    dyn_ptr.to_raw_parts().1
48  };
49}
50
51impl<T: Display + 'static, V: Display + 'static + ?Sized> TraitcastableTo<dyn Cat<V>>
52  for HybridPet<T>
53{
54  const METADATA: ::core::ptr::DynMetadata<dyn Cat<V>> = {
55    let self_ptr: *const HybridPet<T> = ::core::ptr::null::<HybridPet<T>>();
56    let dyn_ptr: *const dyn Cat<V> = self_ptr as _;
57
58    dyn_ptr.to_raw_parts().1
59  };
60}
61
62// The `TARGETS` slice can not be declared inside the `traitcast_targets` function.
63// The "use of generic parameter from outer function" rust limitation is the cause.
64impl<T: Display + 'static> HybridPet<T> {
65  const TARGETS: &[TraitcastTarget] = &[
66    TraitcastTarget::from::<Self, dyn Dog>(),
67    TraitcastTarget::from::<Self, dyn Cat<str>>(),
68  ];
69}
70
71unsafe impl<T: Display + 'static> TraitcastableAny for HybridPet<T> {
72  fn traitcast_targets(&self) -> &[TraitcastTarget] {
73    Self::TARGETS
74  }
75}
76
77#[cfg_attr(test, test)]
78fn main() {
79  // The box is technically not needed but kept for added realism
80  let pet = Box::new(HybridPet {
81    name: "Kokusnuss".to_string(),
82  });
83  pet.greet();
84
85  let castable_pet: Box<dyn TraitcastableAny> = pet;
86
87  let as_dog: &dyn Dog = castable_pet.downcast_ref().unwrap();
88  as_dog.bark();
89
90  let as_cat: &dyn Cat<str> = castable_pet.downcast_ref().unwrap();
91  as_cat.meow("Text");
92
93  let cast_back: &HybridPet<String> = castable_pet.downcast_ref().unwrap();
94  cast_back.greet();
95
96  // Concrete generic `Cat<String>` not specified as a target for `HybridPet<String>`.
97  // Adding `TraitcastTarget::from::<Self, dyn Cat<String>>(),` to the targets would make the cast valid.
98  let invalid_cast: Option<&dyn Cat<String>> = castable_pet.downcast_ref();
99  assert!(invalid_cast.is_none());
100}