tagged_box/
taggable.rs

1#![allow(clippy::module_name_repetitions)]
2
3use crate::tagged_box::TaggedBox;
4
5/// A helper trait for containers that hold a [`TaggedBox`] associated with a specific enum.  
6///
7/// Using this directly is not recommended, as [`tagged_box!`] should be used instead.  
8/// If you want to implement this yourself, see [`manually implementing a tagged enum`].
9///
10/// [`TaggedBox`]: crate::TaggedBox
11/// [`tagged_box!`]: macro.tagged_box.html
12/// [`manually implementing a tagged enum`]: crate::manually_impl_enum
13pub trait TaggableContainer {
14    /// The type of the enum that the container can be safely made into
15    type Inner;
16
17    /// Takes an instance of a `TaggableContainer` and converts it into the enum variant stored within
18    fn into_inner(self) -> Self::Inner;
19}
20
21/// Represents a value able to be stored in a [`TaggedBox`].  
22///
23/// Using this directly is not recommended, as [`tagged_box!`] should be used instead.  
24/// If you want to implement this yourself, see [`manually implementing a tagged enum`].
25///
26/// [`TaggedBox`]: crate::TaggedBox
27/// [`tagged_box!`]: macro.tagged_box.html
28/// [`manually implementing a tagged enum`]: crate::manually_impl_enum
29pub trait TaggableInner: Sized {
30    /// Creates a [`TaggedBox`] from `self`, storing it on the heap and keeping it's discriminant
31    /// in the pointer.  
32    /// See [`TaggedPointer`] for more
33    ///
34    /// [`TaggedBox`]: crate::TaggedBox
35    /// [`TaggedPointer`]: crate::TaggedPointer
36    fn into_tagged_box(self) -> TaggedBox<Self>;
37
38    /// Creates an instance of `Self` from a [`TaggedBox`], taking ownership of the value
39    ///
40    /// [`TaggedBox`]: crate::TaggedBox
41    fn from_tagged_box(tagged: TaggedBox<Self>) -> Self;
42
43    /// Run a closure on a reference to the value contained in `tagged`
44    ///
45    /// # Safety
46    ///
47    /// The closure supplied to `callback` must not move the referenced value
48    ///
49    unsafe fn ref_from_tagged_box<F>(tagged: &TaggedBox<Self>, callback: F)
50    where
51        F: FnOnce(&Self);
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use crate::tagged_box;
58
59    tagged_box! {
60        #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
61        struct Container, enum Item {
62            Int(usize),
63            Bool(bool),
64            Float(f32),
65        }
66    }
67
68    #[test]
69    fn container_into_inner() {
70        let int = Container::from(usize::max_value());
71        assert_eq!(int.into_inner(), Item::Int(usize::max_value()));
72
73        let boolean = Container::from(true);
74        assert_eq!(boolean.into_inner(), Item::Bool(true));
75
76        let float = Container::from(core::f32::MAX);
77        assert_eq!(float.into_inner(), Item::Float(core::f32::MAX));
78    }
79
80    #[test]
81    fn inner_into_tagged_box() {
82        assert_eq!(
83            Item::Int(usize::max_value()),
84            Container {
85                value: Item::Int(usize::max_value()).into_tagged_box()
86            }
87            .into_inner()
88        );
89
90        assert_eq!(
91            Item::Bool(false),
92            Container {
93                value: Item::Bool(false).into_tagged_box()
94            }
95            .into_inner()
96        );
97
98        assert_eq!(
99            Item::Float(core::f32::MIN),
100            Container {
101                value: Item::Float(core::f32::MIN).into_tagged_box()
102            }
103            .into_inner()
104        );
105    }
106
107    #[test]
108    fn inner_from_tagged_box() {
109        assert_eq!(
110            Item::Int(usize::max_value()),
111            Item::from_tagged_box(Item::Int(usize::max_value()).into_tagged_box())
112        );
113
114        assert_eq!(
115            Item::Bool(true),
116            Item::from_tagged_box(Item::Bool(true).into_tagged_box())
117        );
118
119        assert_eq!(
120            Item::Float(core::f32::MAX),
121            Item::from_tagged_box(Item::Float(core::f32::MAX).into_tagged_box())
122        );
123    }
124
125    #[test]
126    fn inner_ref_from_tagged_box() {
127        unsafe {
128            let int = Item::Int(usize::max_value());
129            let boolean = Item::Bool(false);
130            let float = Item::Float(core::f32::MIN);
131
132            Item::ref_from_tagged_box(&Item::Int(usize::max_value()).into_tagged_box(), |item| {
133                assert_eq!(item, &int);
134                assert_ne!(item, &boolean);
135                assert_ne!(item, &float);
136            });
137
138            Item::ref_from_tagged_box(&Item::Bool(false).into_tagged_box(), |item| {
139                assert_eq!(item, &boolean);
140                assert_ne!(item, &int);
141                assert_ne!(item, &float);
142            });
143
144            Item::ref_from_tagged_box(&Item::Float(core::f32::MIN).into_tagged_box(), |item| {
145                assert_eq!(item, &float);
146                assert_ne!(item, &int);
147                assert_ne!(item, &boolean);
148            });
149        }
150    }
151
152    #[test]
153    fn wrapped_refs_from_tagged_box() {
154        let big = Item::Int(10_000).into_tagged_box();
155        let small = Item::Int(100).into_tagged_box();
156
157        unsafe {
158            Item::ref_from_tagged_box(&big, |big| {
159                Item::ref_from_tagged_box(&small, |small| {
160                    assert_ne!(big, small);
161                    assert!(big > small);
162                    assert!(small < big);
163                });
164            });
165        }
166
167        assert_eq!(Item::from_tagged_box(big), Item::Int(10_000));
168        assert_eq!(Item::from_tagged_box(small), Item::Int(100));
169    }
170}