Skip to main content

wingy_core/
casting.rs

1#[macro_export]
2macro_rules! impl_dyn_casting {
3    ($trait:ident) => {
4        impl dyn $trait + '_ {
5            /// Downcast to the backend concrete type.
6            ///
7            /// Returns `None` if the object was not from that backend.
8            pub fn cast_ref<T: $trait>(&self) -> Option<&T> {
9                let this: &dyn std::any::Any = self;
10                this.downcast_ref::<T>()
11            }
12
13            /// Mutable downcast to the backend concrete type.
14            ///
15            /// Returns `None` if the object was not from that backend.
16            pub fn cast_mut<T: $trait>(&mut self) -> Option<&mut T> {
17                let this: &mut dyn std::any::Any = self;
18                this.downcast_mut::<T>()
19            }
20
21            /// Owned downcast to the backend concrete type.
22            ///
23            /// Returns `Err` with `self` if the object was not from that backend.
24            pub fn cast<T: $trait>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
25                if self.cast_ref::<T>().is_some() {
26                    let this: Box<dyn std::any::Any> = self;
27                    // Unwrap is okay, we just checked the type of `self` is `T`.
28                    Ok(this.downcast::<T>().unwrap())
29                } else {
30                    Err(self)
31                }
32            }
33        }
34    };
35}
36
37pub use impl_dyn_casting;
38
39#[cfg(test)]
40mod tests {
41    use std::any::Any;
42
43    struct Foo;
44    trait FooTrait: Any {}
45    impl FooTrait for Foo {}
46    impl_dyn_casting!(FooTrait);
47
48    #[test]
49    fn dyn_casting() {
50        let foo_owned: Box<dyn FooTrait> = Box::new(Foo);
51        assert!(foo_owned.cast::<Foo>().is_ok());
52
53        let mut foo = Foo;
54        let foo_ref: &mut dyn FooTrait = &mut foo;
55        assert!(foo_ref.cast_ref::<Foo>().is_some());
56        let nested_ref: &&&&&mut dyn FooTrait = &&&&foo_ref;
57        assert!(nested_ref.cast_ref::<Foo>().is_some());
58        assert!(foo_ref.cast_mut::<Foo>().is_some());
59    }
60}