const_either/
lib.rs

1//! Some types to allow deciding at compile time if an option contains a value or which variant
2//! from the either type is active. This might be useful when you have some const generic type that
3//! should decide whether to use one datastructure or another, or no datastructure at all.
4//!
5//! # Syntax
6//!
7//! ```ignore
8//! let _definitely_none = ConstOption::<String, false>::new();
9//! let definitely_some = ConstOption::<String, true>::new("hello, world".to_string());
10//!
11//! // When there is definitely some value, the `Deref` trait can be used.
12//! println!("{}", &*definitely_some);
13//!
14//! // Obtain the string inside.
15//! let contained_string = definitely_some.into_inner();
16//!
17//!
18//! struct Container<T, const IS_UNIQUE: bool> {
19//!     data: ConstEither<Vec<T>, HashSet<T>, UNIQUE>,
20//! }
21//!
22//! impl<T> Container<T, false> {
23//!     fn insert(&mut self, val: T) {
24//!         /* ... */
25//!     }
26//! }
27//!
28//! impl<T: Eq + Hash> Container<T, true> {
29//!     fn insert(&mut self, val: T) -> Result<(), T> {
30//!         /* ... */
31//!     }
32//! }
33//! ```
34//!
35//! # Drawbacks
36//!
37//! Because of the current state of rust, the type `ConstEither<L, R>` **will have the size and
38//! alignment of the largest** from `L` and `R`.
39//!
40
41use std::{mem::ManuallyDrop, ops::{Deref, DerefMut}};
42
43/// An `Option` type that is known at compile-time to have or not some value. This is usefull for
44/// writing data structures that use const generics and would like to hold or not a value of some
45/// type based on a compile-time rule.
46///
47/// # Example
48///
49/// ```ignore
50/// struct Person<const HAS_DOG: bool> {
51///     name: String,
52///     age: usize,
53///     dog_name: ConstOption<String, HAS_DOG>,
54/// }
55/// ```
56pub struct ConstOption<T, const IS_SOME: bool>(ConstOptionInner<T, IS_SOME>);
57
58union ConstOptionInner<T, const IS_SOME: bool> {
59    none: (),
60    some: ManuallyDrop<T>,
61}
62
63impl<T> ConstOption<T, false> {
64    pub fn new() -> Self {
65        ConstOption(ConstOptionInner { none: () })
66    }
67}
68
69impl<T> ConstOption<T, true> {
70    pub fn new(val: T) -> Self {
71        ConstOption(ConstOptionInner { some: ManuallyDrop::new(val) })
72    }
73
74    pub fn into_inner(mut self) -> T {
75        unsafe { ManuallyDrop::take(&mut self.0.some) }
76    }
77}
78
79impl<T, const IS_SOME: bool> Drop for ConstOption<T, IS_SOME> {
80    fn drop(&mut self) {
81        unsafe {
82            if IS_SOME {
83                drop(ManuallyDrop::take(&mut self.0.some))
84            }
85        }
86    }
87}
88
89impl<T> AsRef<T> for ConstOption<T, true> {
90    fn as_ref(&self) -> &T {
91        unsafe { &self.0.some }
92    }
93}
94
95impl<T> AsMut<T> for ConstOption<T, true> {
96    fn as_mut(&mut self) -> &mut T {
97        unsafe { &mut self.0.some }
98    }
99}
100
101impl<T> Deref for ConstOption<T, true> {
102    type Target = T;
103
104    fn deref(&self) -> &Self::Target {
105        self.as_ref()
106    }
107}
108
109impl<T> DerefMut for ConstOption<T, true> {
110    fn deref_mut(&mut self) -> &mut Self::Target {
111        self.as_mut()
112    }
113}
114
115/// An `Either` type that is known to hold a left or right value at compile-time. This allows data
116/// structures choose an appropriate type based on some compile-time determined policy.
117///
118/// # Example
119///
120/// ```ignore
121/// struct TheOneVec<T, const INLINE: bool, const MIN_CAPACITY: usize> {
122///     data: ConstEither<Vec<T>, tinyvec::ArrayVec<[T; MIN_CAPACITY]>, INLINE>,
123/// }
124///
125/// impl<T, const MIN_CAPACITY: usize> TheOneVec<T, false, MIN_CAPACITY> {
126///     fn new() -> Self {
127///         TheOneVec { data: ConstEither::new(Vec::with_capacity(MIN_CAPACITY)) }
128///     }
129/// }
130///
131/// impl<T, const MIN_CAPACITY: usize> TheOneVec<T, true, MIN_CAPACITY> {
132///     fn new() -> Self {
133///         TheOneVec { data: ConstEither::new(tinyvec::ArrayVec::new()) }
134///     }
135/// }
136///
137/// struct TheOneString<const INLINE: bool>(TheOneVec<u8, INLINE, 32>);
138///
139/// struct MaybeHeaplessPerson<const INLINE: bool> {
140///     name: TheOneString<INLINE>,
141///     age: usize,
142///     hobbies: TheOneVec<TheOneString<INLINE>, INLINE, 16>,
143/// }
144/// ```
145pub struct ConstEither<L, R, const IS_RIGHT: bool>(ConstEitherInner<L, R, IS_RIGHT>);
146
147union ConstEitherInner<L, R, const IS_RIGHT: bool> {
148    left: ManuallyDrop<L>,
149    right: ManuallyDrop<R>,
150}
151
152
153impl<L, R> ConstEither<L, R, false> {
154    pub fn new(left: L) -> Self {
155        ConstEither(ConstEitherInner { left: ManuallyDrop::new(left) })
156    }
157
158    pub fn into_inner(mut self) -> L {
159        unsafe { ManuallyDrop::take(&mut self.0.left) }
160    }
161
162    pub fn flip(self) -> ConstEither<R, L, true> {
163        let val = self.into_inner();
164        ConstEither::<R, L, true>::new(val)
165    }
166}
167
168impl<L, R> ConstEither<L, R, true> {
169    pub fn new(right: R) -> Self {
170        ConstEither(ConstEitherInner { right: ManuallyDrop::new(right) })
171    }
172
173    pub fn into_inner(mut self) -> R {
174        unsafe { ManuallyDrop::take(&mut self.0.right) }
175    }
176
177    pub fn flip(self) -> ConstEither<R, L, false> {
178        let val = self.into_inner();
179        ConstEither::<R, L, false>::new(val)
180    }
181}
182
183impl<L, R> AsRef<L> for ConstEither<L, R, false> {
184    fn as_ref(&self) -> &L {
185        unsafe { &self.0.left }
186    }
187}
188
189impl<L, R> AsRef<R> for ConstEither<L, R, true> {
190    fn as_ref(&self) -> &R {
191        unsafe { &self.0.right }
192    }
193}
194
195impl<L, R> AsMut<L> for ConstEither<L, R, false> {
196    fn as_mut(&mut self) -> &mut L {
197        unsafe { &mut self.0.left }
198    }
199}
200
201impl<L, R> AsMut<R> for ConstEither<L, R, true> {
202    fn as_mut(&mut self) -> &mut R {
203        unsafe { &mut self.0.right }
204    }
205}
206
207impl<L, R> Deref for ConstEither<L, R, false> {
208    type Target = L;
209
210    fn deref(&self) -> &Self::Target {
211        self.as_ref()
212    }
213}
214
215impl<L, R> Deref for ConstEither<L, R, true> {
216    type Target = R;
217
218    fn deref(&self) -> &Self::Target {
219        self.as_ref()
220    }
221}
222
223impl<L, R> DerefMut for ConstEither<L, R, false> {
224    fn deref_mut(&mut self) -> &mut Self::Target {
225        self.as_mut()
226    }
227}
228
229impl<L, R> DerefMut for ConstEither<L, R, true> {
230    fn deref_mut(&mut self) -> &mut Self::Target {
231        self.as_mut()
232    }
233}
234
235impl<L, R, const IS_RIGHT: bool> Drop for ConstEither<L, R, IS_RIGHT> {
236    fn drop(&mut self) {
237        unsafe {
238            if IS_RIGHT {
239                drop(ManuallyDrop::take(&mut self.0.right));
240            } else {
241                drop(ManuallyDrop::take(&mut self.0.left));
242            }
243        }
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use std::convert::Infallible;
250
251    use super::*;
252
253    #[test]
254    fn nothing() {
255        let _none = ConstOption::<Infallible, false>::new();
256        let mut right = ConstEither::<Infallible, usize, true>::new(1234);
257
258        assert_eq!(*right, 1234);
259        *right = 456;
260        assert_eq!(right.into_inner(), 456);
261    }
262
263    #[test]
264    fn something() {
265        let some = ConstOption::<String, true>::new("Hello, world".to_string());
266        assert_eq!(*some, "Hello, world");
267    }
268}