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