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}