manual/
manual.rs

1//! This example demonstrates how to manually implement the `TraitcastableAny` trait for a non 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(trait_upcasting)]
9#![allow(incomplete_features)]
10#![feature(ptr_metadata)]
11
12use trait_cast::{
13  TraitcastTarget, TraitcastableAny, TraitcastableAnyInfra, TraitcastableAnyInfraExt,
14  TraitcastableTo,
15};
16
17struct HybridPet {
18  name: String,
19}
20impl TraitcastableTo<dyn Dog> for HybridPet {
21  const METADATA: ::core::ptr::DynMetadata<dyn Dog> = {
22    let self_ptr: *const HybridPet = ::core::ptr::null::<HybridPet>();
23    let dyn_ptr: *const dyn Dog = self_ptr as _;
24
25    dyn_ptr.to_raw_parts().1
26  };
27}
28
29impl TraitcastableTo<dyn Cat> for HybridPet {
30  const METADATA: ::core::ptr::DynMetadata<dyn Cat> = {
31    let self_ptr: *const Self = ::core::ptr::null::<Self>();
32    let dyn_ptr: *const dyn Cat = self_ptr as _;
33
34    dyn_ptr.to_raw_parts().1
35  };
36}
37
38unsafe impl TraitcastableAny for HybridPet {
39  fn traitcast_targets(&self) -> &[TraitcastTarget] {
40    const TARGETS: &[TraitcastTarget] = &[
41      TraitcastTarget::from::<HybridPet, dyn Dog>(),
42      TraitcastTarget::from::<HybridPet, dyn Cat>(),
43    ];
44    TARGETS
45  }
46}
47impl HybridPet {
48  fn greet(&self) {
49    println!("{}: Hi", self.name);
50  }
51}
52
53impl Dog for HybridPet {
54  fn bark(&self) {
55    println!("{}: Woof!", self.name);
56  }
57}
58impl Cat for HybridPet {
59  fn meow(&self) {
60    println!("{}: Meow!", self.name);
61  }
62}
63
64trait Dog {
65  fn bark(&self);
66}
67trait Cat: TraitcastableAny {
68  fn meow(&self);
69}
70trait Mouse {}
71
72#[cfg_attr(test, test)]
73fn main() {
74  // The box is technically not needed but kept for added realism
75  let pet = Box::new(HybridPet {
76    name: "Kokusnuss".to_string(),
77  });
78  pet.greet();
79
80  let castable_pet: Box<dyn TraitcastableAny> = pet;
81
82  let as_dog: &dyn Dog = castable_pet.downcast_ref().unwrap();
83  as_dog.bark();
84
85  let as_cat: &dyn Cat = castable_pet.downcast_ref().unwrap();
86  as_cat.meow();
87
88  let cast_back: &HybridPet = castable_pet.downcast_ref().unwrap();
89  cast_back.greet();
90
91  // upcasting examples
92  // require feature flag trait_upcasting
93  // you must also add TraitcastableAny to your trait
94  let upcast_ref: &dyn TraitcastableAny = as_cat;
95  let downcast_to_cat_again: &dyn Cat = upcast_ref.downcast_ref().unwrap();
96  downcast_to_cat_again.meow();
97
98  let as_box_cat: Box<dyn Cat> = castable_pet.downcast().unwrap();
99  let castable_pet: Box<dyn TraitcastableAny> = as_box_cat;
100
101  // failed cast example
102  // shows how to recover the box without dropping it
103  let no_mouse: Result<Box<dyn Mouse>, _> = castable_pet.downcast();
104  if let Err(no_mouse) = no_mouse {
105    let as_cat: &dyn Cat = no_mouse.downcast_ref().unwrap();
106    as_cat.meow();
107  }
108}