anybox/
lib.rs

1#[cfg(test)]
2mod tests {
3    use super::AnyBox;
4
5    macro_rules! typed_test {
6        ($name:ident, $ty:ty, $value:expr) => {
7            #[test]
8            fn $name() {
9                let data: $ty = $value;
10                let expected: $ty = $value;
11
12                let anybox = AnyBox::new(data);
13
14                assert_eq!(anybox.get::<$ty>(), &expected);
15                assert_eq!(anybox.try_get::<()>(), None);
16            }
17        };
18    }
19
20    typed_test!(byte, u8, 1);
21    typed_test!(byte2, u8, 123);
22
23    typed_test!(usize1, usize, 1);
24    typed_test!(usize2, usize, 2);
25    typed_test!(usize3, usize, 26387890);
26    typed_test!(usize4, usize, 123789123);
27    typed_test!(usize5, usize, 3279173921873921);
28
29    typed_test!(isize1, isize, 1);
30    typed_test!(isize2, isize, 2);
31    typed_test!(isize3, isize, 26387890);
32    typed_test!(isize4, isize, 123789123);
33    typed_test!(isize5, isize, 3279173921873921);
34    typed_test!(neg_isize1, isize, -1);
35    typed_test!(neg_isize2, isize, -2);
36    typed_test!(neg_isize3, isize, -26387890);
37    typed_test!(neg_isize4, isize, -123789123);
38    typed_test!(neg_isize5, isize, -3279173921873921);
39}
40
41use std::any;
42
43/// Container for values of any type.
44/// When retrieving values, correct type has to be specified.
45/// TODO: I'm reasonably certain that drop leaks memory and won't drop inner vaules,
46/// creating all sorts of nastiness
47pub struct AnyBox {
48    ty: any::TypeId,
49    // not really pointing to values of type u8.
50    // rather it's used as a pointer to any sort of data.
51    value: Box<u8>,
52}
53
54impl AnyBox {
55    pub fn new<T: 'static>(value: T) -> Self {
56        let ty = any::TypeId::of::<T>();
57
58        // place value on the heap
59        let value = Box::new(value);
60        let value = unsafe { std::mem::transmute::<Box<T>, Box<u8>>(value) };
61        Self { ty, value }
62    }
63
64    /// get a reference to the held value or None
65    pub fn try_get<'a, T: 'static>(&'a self) -> Option<&'a T> {
66        let ty = any::TypeId::of::<T>();
67
68        if ty != self.ty {
69            return None;
70        }
71
72        let value = unsafe { std::mem::transmute::<&u8, &T>(self.value.as_ref()) };
73
74        Some(value)
75    }
76
77    /// get a reference to the held value or panic
78    pub fn get<'a, T: 'static>(&'a self) -> &T {
79        match self.try_get::<T>() {
80            None => {
81                let full_name = any::type_name::<T>();
82                panic!("Attempted to take value as forgein type {}", full_name);
83            }
84            Some(t) => t,
85        }
86    }
87
88    pub fn put<T: 'static>(&mut self, value: T) {
89        let ty = any::TypeId::of::<T>();
90
91        if ty != self.ty {
92            return;
93        }
94
95        let mut value = Box::new(value);
96        // now switch positions of the inner and passed values.
97        // That way the inner value can be properly deallocated.
98        {
99            let inner = self.value.as_mut();
100            let mut inner = unsafe {
101                std::mem::transmute::<&mut u8, &mut T>(inner)
102            };
103
104            std::mem::swap(value.as_mut(), &mut inner);
105        }
106    }
107}