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}