actix_web/error/
macros.rs

1macro_rules! downcast_get_type_id {
2    () => {
3        /// A helper method to get the type ID of the type
4        /// this trait is implemented on.
5        /// This method is unsafe to *implement*, since `downcast_ref` relies
6        /// on the returned `TypeId` to perform a cast.
7        ///
8        /// Unfortunately, Rust has no notion of a trait method that is
9        /// unsafe to implement (marking it as `unsafe` makes it unsafe
10        /// to *call*). As a workaround, we require this method
11        /// to return a private type along with the `TypeId`. This
12        /// private type (`PrivateHelper`) has a private constructor,
13        /// making it impossible for safe code to construct outside of
14        /// this module. This ensures that safe code cannot violate
15        /// type-safety by implementing this method.
16        ///
17        /// We also take `PrivateHelper` as a parameter, to ensure that
18        /// safe code cannot obtain a `PrivateHelper` instance by
19        /// delegating to an existing implementation of `__private_get_type_id__`
20        #[doc(hidden)]
21        #[allow(dead_code)]
22        fn __private_get_type_id__(&self, _: PrivateHelper) -> (std::any::TypeId, PrivateHelper)
23        where
24            Self: 'static,
25        {
26            (std::any::TypeId::of::<Self>(), PrivateHelper(()))
27        }
28    };
29}
30
31// Generate implementation for dyn $name
32macro_rules! downcast_dyn {
33    ($name:ident) => {
34        /// A struct with a private constructor, for use with
35        /// `__private_get_type_id__`. Its single field is private,
36        /// ensuring that it can only be constructed from this module
37        #[doc(hidden)]
38        #[allow(dead_code)]
39        pub struct PrivateHelper(());
40
41        impl dyn $name + 'static {
42            /// Downcasts generic body to a specific type.
43            #[allow(dead_code)]
44            pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
45                if self.__private_get_type_id__(PrivateHelper(())).0 == std::any::TypeId::of::<T>()
46                {
47                    // SAFETY: external crates cannot override the default
48                    // implementation of `__private_get_type_id__`, since
49                    // it requires returning a private type. We can therefore
50                    // rely on the returned `TypeId`, which ensures that this
51                    // case is correct.
52                    unsafe { Some(&*(self as *const dyn $name as *const T)) }
53                } else {
54                    None
55                }
56            }
57
58            /// Downcasts a generic body to a mutable specific type.
59            #[allow(dead_code)]
60            pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
61                if self.__private_get_type_id__(PrivateHelper(())).0 == std::any::TypeId::of::<T>()
62                {
63                    // SAFETY: external crates cannot override the default
64                    // implementation of `__private_get_type_id__`, since
65                    // it requires returning a private type. We can therefore
66                    // rely on the returned `TypeId`, which ensures that this
67                    // case is correct.
68                    unsafe { Some(&mut *(self as *const dyn $name as *const T as *mut T)) }
69                } else {
70                    None
71                }
72            }
73        }
74    };
75}
76
77pub(crate) use downcast_dyn;
78pub(crate) use downcast_get_type_id;
79
80#[cfg(test)]
81mod tests {
82    #![allow(clippy::upper_case_acronyms)]
83
84    trait MB {
85        downcast_get_type_id!();
86    }
87
88    downcast_dyn!(MB);
89
90    impl MB for String {}
91    impl MB for () {}
92
93    #[actix_rt::test]
94    async fn test_any_casting() {
95        let mut body = String::from("hello cast");
96        let resp_body: &mut dyn MB = &mut body;
97        let body = resp_body.downcast_ref::<String>().unwrap();
98        assert_eq!(body, "hello cast");
99        let body = resp_body.downcast_mut::<String>().unwrap();
100        body.push('!');
101        let body = resp_body.downcast_ref::<String>().unwrap();
102        assert_eq!(body, "hello cast!");
103        let not_body = resp_body.downcast_ref::<()>();
104        assert!(not_body.is_none());
105    }
106}