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
///! Rust enums are great for types where all variations are known beforehand. But in ///! the case where you want to implement a container of user-defined types, an ///! open-ended type like a trait object is needed. In some cases, it is useful to ///! cast the trait object back into its original concrete type to access additional ///! functionality and performant inlined implementations. ///! ///! `downcast` is an exercise in adding basic down-casting support to trait objects ///! just as [MOPA](https://crates.io/crates/mopa/) and ///! [downcast](https://crates.io/crates/downcast/) do while avoiding unsafe code and ///! without replicating the behavior in the standard library. This is at the ///! (negligible) expense of adding two methods to the down-castable trait's vtable. ///! ///! To make a trait downcastable, make it extend the `downcast::Downcast` trait and ///! invoke `downcast_impl!` on it as follows: ///! ///! ```rust ///! trait Trait: Downcast {} ///! downcast_impl!(Trait); ///! ``` ///! ///! # Example ///! ///! ```rust ///! #[macro_use] ///! extern crate downcast_rs; ///! use downcast_rs::Downcast; ///! ///! // To create a trait with downcasting methods, extend `Downcast` and run ///! // downcast_impl!() on the trait. ///! trait Base: Downcast {} ///! downcast_impl!(Base); ///! ///! // Concrete type implementing Base. ///! struct Foo(u32); ///! impl Base for Foo {} ///! ///! fn main() { ///! // Create a trait object. ///! let mut base: Box<Base> = Box::new(Foo(42)); ///! ///! // Downcast to Foo. ///! assert_eq!(base.downcast_ref::<Foo>().unwrap().0, 42); ///! } ///! ``` use std::any::Any; /// Supports conversion to `Any`. Traits to be extended by `downcast_impl!` must extend `Downcast`. pub trait Downcast: Any { fn as_any(&self) -> &Any; fn as_any_mut(&mut self) -> &mut Any; } impl<T: Any> Downcast for T { fn as_any(&self) -> &Any { self } fn as_any_mut(&mut self) -> &mut Any { self } } /// Adds downcasting support to traits that extend `downcast::Downcast` by defining forwarding /// methods to the corresponding implementations on `std::any::Any` in the standard library. #[macro_export] macro_rules! downcast_impl { ($trait_:ident) => { impl $trait_ { /// Returns true if the boxed type is the same as `T`. #[inline] pub fn is<T: $trait_>(&self) -> bool { $crate::Downcast::as_any(self).is::<T>() } /// Returns a reference to the boxed value if it is of type `T`, or /// `None` if it isn't. #[inline] pub fn downcast_ref<T: $trait_>(&self) -> Option<&T> { $crate::Downcast::as_any(self).downcast_ref::<T>() } /// Returns a mutable reference to the boxed value if it is of type /// `T`, or `None` if it isn't. #[inline] pub fn downcast_mut<T: $trait_>(&mut self) -> Option<&mut T> { $crate::Downcast::as_any_mut(self).downcast_mut::<T>() } } } } #[cfg(test)] mod test { use super::Downcast; // A trait that can be downcast. trait Base: Downcast {} downcast_impl!(Base); // Concrete type implementing Base. struct Foo(u32); impl Base for Foo {} // Functions that can work on references to Base trait objects. fn get_val(base: &Box<Base>) -> u32 { match base.downcast_ref::<Foo>() { Some(val) => val.0, None => 0 } } fn set_val(base: &mut Box<Base>, val: u32) { if let Some(foo) = base.downcast_mut::<Foo>() { foo.0 = val; } } #[test] fn test() { let mut base: Box<Base> = Box::new(Foo(42)); assert_eq!(get_val(&base), 42); set_val(&mut base, 6*9); assert_eq!(get_val(&base), 6*9); assert!(base.is::<Foo>()); } }