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
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: Clone + '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 () {}

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

#[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
    }
}

#[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);
    }
}