downcast_trait/
lib.rs

1#![no_std]
2#![allow(unused_imports)]
3//!
4//! Downcast trait: A module to support downcasting dyn traits using [core::any].
5//! This trait is similar to [intertrait](https://crates.io/crates/intertrait), but does not require
6//! to make a hashtable or any fancy linker magic. For certain cases all casting is optimized away.
7//!
8//! This crate uses transmute (which is generally considered unsafe rust) to pass an unknown type
9//!  as a return value from a function, but the value is then transmuted back to the original type.
10//!
11//! Downcast traits enables callers to convert dyn objects that implement the
12//! DowncastTrait trait to any trait that is supported by the struct implementing the trait.
13//! The most useful usecase for this is if a class contains a list of objects that implements a
14//! trait and want to call functions on a subset which implements another trait too. This is similar
15//! to casting to a sub-class in an object oriented language.
16//!
17//! Example:
18//! * A Widget trait is implemented for all widgets in a graphical user interface system.
19//! * The widget trait extends the DowncastTrait.
20//! * A widget may implement the Container trait if it is possible to add child widgets to the widget.
21//! * A container has a list of widgets, and want to call a specific functions on all widgets that
22//!   implement container.
23//!
24//! ```
25//! #[macro_use] extern crate downcast_trait;
26//! use downcast_trait::DowncastTrait;
27//! use core::{any::{Any, TypeId}, mem};
28//! trait Widget: DowncastTrait {}
29//! trait Container: Widget {
30//!     fn enumerate_widget_leaves_recursive(&self) -> Vec<&Box<dyn Widget>>;
31//! }
32//! struct Window {
33//!     sub_widgets: Vec<Box<dyn Widget>>,
34//! }
35//! impl Widget for Window {}
36//! impl Container for Window {
37//!     fn enumerate_widget_leaves_recursive(&self) -> Vec<&Box<dyn Widget>> {
38//!         let mut result = Vec::<&Box<dyn Widget>>::new();
39//!         self.sub_widgets.iter().for_each(|sub_widget| {
40//!             if let Some(sub_container) =
41//!                 downcast_trait!(dyn Container, sub_widget.as_ref().to_downcast_trait())
42//!             {
43//!                 result.extend(sub_container.enumerate_widget_leaves_recursive());
44//!             } else {
45//!                 result.push(sub_widget);
46//!             }
47//!         });
48//!         result
49//!     }
50//! }
51//! impl DowncastTrait for Window {
52//!     downcast_trait_impl_convert_to!(dyn Container);
53//! }
54//! ```
55use core::{
56    any::{Any, TypeId},
57    mem,
58};
59
60/// This trait should be implemented by any structs that or traits that should be downcastable
61/// to dowcast to one or more traits. The functions required by this trait should be implemented
62/// using the [downcast_trait_impl_convert_to](macro.downcast_trait_impl_convert_to.html) macro.
63/// ```ignore
64/// trait Widget: DowncastTrait {}
65/// ```
66pub trait DowncastTrait {
67    /// # Safety
68    /// This function is called by the [downcast_trait](macro.downcast_trait.html) macro and should
69    /// not be accessed directly.
70    unsafe fn convert_to_trait(&self, trait_id: TypeId) -> Option<&(dyn Any)>;
71    /// # Safety
72    /// This function is called by the [downcast_trait_mut](macro.downcast_trait_mut.html) macro
73    /// and should not be accessed directly.
74    unsafe fn convert_to_trait_mut(&mut self, trait_id: TypeId) -> Option<&mut (dyn Any)>;
75    /// This macro is used to cast any implementer of this trait to a DowncastTrait
76    fn to_downcast_trait(&self) -> &dyn DowncastTrait;
77    /// This macro is used to cast any implementer of this trait to a mut DowncastTrait
78    fn to_downcast_trait_mut(&mut self) -> &mut dyn DowncastTrait;
79}
80
81/// This macro can be used to cast a &dyn DowncastTrait to an implemented trait e.g:
82/// ```ignore
83/// if let Some(sub_container) =
84///     downcast_trait!(dyn Container, sub_widget.as_ref().to_downcast_trait())
85/// {
86///   //Use downcasted trait
87/// }
88/// ```
89#[macro_export]
90macro_rules! downcast_trait {
91    ( dyn $type:path, $src:expr) => {{
92        fn transmute_helper(src: &dyn DowncastTrait) -> Option<&dyn $type> {
93            unsafe {
94                src.convert_to_trait(TypeId::of::<dyn $type>())
95                    .map(|dst| mem::transmute::<&(dyn Any), &(dyn $type)>(dst))
96            }
97        }
98        transmute_helper($src)
99    }};
100}
101
102/// This macro can be used to cast a &dyn mut DowncastTrait to an implemented trait e.g:
103/// ```ignore
104/// if let Some(sub_container) =
105///     downcast_trait_mut!(dyn Container, sub_widget.as_ref().to_downcast_trait())
106/// {
107///   //Use downcasted trait
108/// }
109/// ```
110#[macro_export]
111macro_rules! downcast_trait_mut {
112    ( dyn $type:path, $src:expr) => {{
113        fn transmute_helper(src: &mut dyn DowncastTrait) -> Option<&mut dyn $type> {
114            unsafe {
115                src.convert_to_trait_mut(TypeId::of::<dyn $type>())
116                    .map(|dst| mem::transmute::<&mut (dyn Any), &mut (dyn $type)>(dst))
117            }
118        }
119        transmute_helper($src)
120    }};
121}
122/// This macro can be used by a struct impl, to implement the functions required by the downcas traitt
123/// to dowcast to one or more traits.
124/// ```ignore
125/// impl DowncastTrait for Window {
126///     downcast_trait_impl_convert_to!(dyn Container, dyn Scrollable, dyn Clickable);
127/// }
128/// ```
129
130#[macro_export]
131macro_rules! downcast_trait_impl_convert_to
132{
133    ($(dyn $type:path),+) => {
134        unsafe fn convert_to_trait(& self, trait_id: TypeId) -> Option<& (dyn Any)> {
135            if false
136            {
137               None
138            }
139            $(
140            else if trait_id == TypeId::of::<dyn $type>()
141            {
142                Some(mem::transmute::<& (dyn $type), & dyn Any>(
143                    self as & (dyn $type)
144                ))
145            }
146            )*
147            else
148            {
149                None
150            }
151        }
152
153        unsafe fn convert_to_trait_mut(& mut self, trait_id: TypeId) -> Option<& mut (dyn Any)> {
154            if false
155            {
156               None
157            }
158            $(
159            else if trait_id == TypeId::of::<dyn $type>()
160            {
161                Some(mem::transmute::<& mut (dyn $type), & mut dyn Any>(
162                    self as & mut (dyn $type)
163                ))
164            }
165            )*
166            else
167            {
168                None
169            }
170        }
171
172        fn to_downcast_trait(& self) -> & dyn DowncastTrait
173        {
174            self
175        }
176
177        fn to_downcast_trait_mut(& mut self) -> & mut dyn DowncastTrait
178        {
179            self
180        }
181    };
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    trait Downcasted {
188        fn get_number(&self) -> u32;
189    }
190    trait Downcasted2 {
191        fn get_number(&self) -> u32;
192    }
193    struct Downcastable {
194        val: u32,
195    }
196    impl Downcasted for Downcastable {
197        fn get_number(&self) -> u32 {
198            self.val + 123
199        }
200    }
201    impl Downcasted2 for Downcastable {
202        fn get_number(&self) -> u32 {
203            self.val + 456
204        }
205    }
206    impl DowncastTrait for Downcastable {
207        downcast_trait_impl_convert_to!(dyn Downcasted, dyn Downcasted2);
208    }
209
210    #[test]
211    fn exploration() {
212        let mut tst = Downcastable { val: 0 };
213        let ts: &mut dyn DowncastTrait = tst.to_downcast_trait_mut();
214        let downcasted_maybe = downcast_trait!(dyn Downcasted, ts);
215        if let Some(downcasted) = downcasted_maybe {
216            assert_eq!(downcasted.get_number(), 123);
217        }
218        let downcasted_maybe2 = downcast_trait!(dyn Downcasted2, ts);
219        if let Some(downcasted2) = downcasted_maybe2 {
220            assert_eq!(downcasted2.get_number(), 456);
221        }
222
223        let mut downcasted_maybemut = downcast_trait_mut!(dyn Downcasted2, ts);
224        match &mut downcasted_maybemut {
225            Some(downcasted_mut) => {
226                assert_eq!(downcasted_mut.get_number(), 456);
227            }
228            None => assert!(false),
229        }
230    }
231}