multiversx_sc_codec/
try_static_cast.rs

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use core::any::TypeId;

/// Use to transfer objects from one generic type to another,
/// without the compiler being able to determine whether or not the two types are the same.
/// The cast is statically dispatched.
pub trait TryStaticCast: Sized + 'static {
    fn type_eq<U: TryStaticCast>() -> bool {
        TypeId::of::<Self>() == TypeId::of::<U>()
    }

    #[inline]
    fn try_cast<U: TryStaticCast>(self) -> Option<U> {
        if Self::type_eq::<U>() {
            let trans: U = unsafe { core::mem::transmute_copy(&self) };
            core::mem::forget(self);
            Some(trans)
        } else {
            None
        }
    }

    #[inline]
    fn try_cast_ref<U: TryStaticCast>(&self) -> Option<&U> {
        if Self::type_eq::<U>() {
            let trans = unsafe { core::mem::transmute::<&Self, &U>(self) };
            Some(trans)
        } else {
            None
        }
    }
}

impl TryStaticCast for () {}
impl TryStaticCast for i32 {}

fn type_eq<T, U>() -> bool
where
    T: 'static,
    U: 'static,
{
    TypeId::of::<T>() == TypeId::of::<U>()
}

#[inline]
pub fn try_cast_ref<T, U>(t: &T) -> Option<&U>
where
    T: 'static,
    U: 'static,
{
    if type_eq::<T, U>() {
        let trans = unsafe { core::mem::transmute::<&T, &U>(t) };
        Some(trans)
    } else {
        None
    }
}

#[inline]
pub fn try_execute_then_cast<T, R, F>(f: F) -> Option<R>
where
    T: 'static,
    R: 'static,
    F: FnOnce() -> T,
{
    if type_eq::<T, R>() {
        let result: T = f();
        let transmuted_result: R = unsafe { core::mem::transmute_copy(&result) };
        core::mem::forget(result);
        Some(transmuted_result)
    } else {
        None
    }
}

#[inline]
pub fn try_cast_execute_or_else<T, U, R, If, Else>(t: T, exec_if: If, exec_else: Else) -> R
where
    T: 'static,
    U: 'static,
    R: 'static,
    If: FnOnce(U) -> R,
    Else: FnOnce(T) -> R,
{
    if type_eq::<T, U>() {
        let transmuted: U = unsafe { core::mem::transmute_copy(&t) };
        core::mem::forget(t);
        exec_if(transmuted)
    } else {
        exec_else(t)
    }
}

#[cfg(test)]
mod test {
    use super::TryStaticCast;

    #[derive(Clone, PartialEq, Eq, Debug)]
    struct SimpleType1(i32);

    impl TryStaticCast for SimpleType1 {}

    #[derive(Clone, PartialEq, Eq, Debug)]
    struct SimpleType2(i32);

    impl TryStaticCast for SimpleType2 {}

    #[derive(Clone, PartialEq, Eq, Debug)]
    struct GenericType<T> {
        id: i32,
        payload: T,
    }

    impl<T> GenericType<T> {
        fn new(id: i32, payload: T) -> Self {
            GenericType { id, payload }
        }
    }

    impl<T: Clone + 'static> TryStaticCast for GenericType<T> {}

    #[test]
    fn test_try_static_cast_simple() {
        let obj = SimpleType1(5);
        assert_eq!(obj.clone().try_cast::<SimpleType1>(), Some(obj.clone()));
        assert_eq!(obj.clone().try_cast::<SimpleType2>(), None);

        assert_eq!(obj.try_cast_ref::<SimpleType1>(), Some(&obj));
        assert_eq!(obj.try_cast_ref::<SimpleType2>(), None);
    }

    #[test]
    fn test_try_static_cast_with_generics() {
        let obj = GenericType::new(100, SimpleType1(5));
        assert_eq!(
            obj.clone().try_cast::<GenericType<SimpleType1>>(),
            Some(obj.clone())
        );
        assert_eq!(obj.try_cast::<GenericType<SimpleType2>>(), None);
    }
}