multiversx_sc_codec/
try_static_cast.rs

1use core::any::TypeId;
2
3/// Use to transfer objects from one generic type to another,
4/// without the compiler being able to determine whether or not the two types are the same.
5/// The cast is statically dispatched.
6pub trait TryStaticCast: Sized + 'static {
7    fn type_eq<U: TryStaticCast>() -> bool {
8        TypeId::of::<Self>() == TypeId::of::<U>()
9    }
10
11    #[inline]
12    fn try_cast<U: TryStaticCast>(self) -> Option<U> {
13        if Self::type_eq::<U>() {
14            let trans: U = unsafe { core::mem::transmute_copy(&self) };
15            core::mem::forget(self);
16            Some(trans)
17        } else {
18            None
19        }
20    }
21
22    #[inline]
23    fn try_cast_ref<U: TryStaticCast>(&self) -> Option<&U> {
24        if Self::type_eq::<U>() {
25            let trans = unsafe { core::mem::transmute::<&Self, &U>(self) };
26            Some(trans)
27        } else {
28            None
29        }
30    }
31}
32
33impl TryStaticCast for () {}
34impl TryStaticCast for i32 {}
35
36fn type_eq<T, U>() -> bool
37where
38    T: 'static,
39    U: 'static,
40{
41    TypeId::of::<T>() == TypeId::of::<U>()
42}
43
44#[inline]
45pub fn try_cast_ref<T, U>(t: &T) -> Option<&U>
46where
47    T: 'static,
48    U: 'static,
49{
50    if type_eq::<T, U>() {
51        let trans = unsafe { core::mem::transmute::<&T, &U>(t) };
52        Some(trans)
53    } else {
54        None
55    }
56}
57
58#[inline]
59pub fn try_execute_then_cast<T, R, F>(f: F) -> Option<R>
60where
61    T: 'static,
62    R: 'static,
63    F: FnOnce() -> T,
64{
65    if type_eq::<T, R>() {
66        let result: T = f();
67        let transmuted_result: R = unsafe { core::mem::transmute_copy(&result) };
68        core::mem::forget(result);
69        Some(transmuted_result)
70    } else {
71        None
72    }
73}
74
75#[inline]
76pub fn try_cast_execute_or_else<T, U, R, If, Else>(t: T, exec_if: If, exec_else: Else) -> R
77where
78    T: 'static,
79    U: 'static,
80    R: 'static,
81    If: FnOnce(U) -> R,
82    Else: FnOnce(T) -> R,
83{
84    if type_eq::<T, U>() {
85        let transmuted: U = unsafe { core::mem::transmute_copy(&t) };
86        core::mem::forget(t);
87        exec_if(transmuted)
88    } else {
89        exec_else(t)
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use super::TryStaticCast;
96
97    #[derive(Clone, PartialEq, Eq, Debug)]
98    struct SimpleType1(i32);
99
100    impl TryStaticCast for SimpleType1 {}
101
102    #[derive(Clone, PartialEq, Eq, Debug)]
103    struct SimpleType2(i32);
104
105    impl TryStaticCast for SimpleType2 {}
106
107    #[derive(Clone, PartialEq, Eq, Debug)]
108    struct GenericType<T> {
109        id: i32,
110        payload: T,
111    }
112
113    impl<T> GenericType<T> {
114        fn new(id: i32, payload: T) -> Self {
115            GenericType { id, payload }
116        }
117    }
118
119    impl<T: Clone + 'static> TryStaticCast for GenericType<T> {}
120
121    #[test]
122    fn test_try_static_cast_simple() {
123        let obj = SimpleType1(5);
124        assert_eq!(obj.clone().try_cast::<SimpleType1>(), Some(obj.clone()));
125        assert_eq!(obj.clone().try_cast::<SimpleType2>(), None);
126
127        assert_eq!(obj.try_cast_ref::<SimpleType1>(), Some(&obj));
128        assert_eq!(obj.try_cast_ref::<SimpleType2>(), None);
129    }
130
131    #[test]
132    fn test_try_static_cast_with_generics() {
133        let obj = GenericType::new(100, SimpleType1(5));
134        assert_eq!(
135            obj.clone().try_cast::<GenericType<SimpleType1>>(),
136            Some(obj.clone())
137        );
138        assert_eq!(obj.try_cast::<GenericType<SimpleType2>>(), None);
139    }
140}