controlled_option/
lib.rs

1// -*- coding: utf-8 -*-
2// ------------------------------------------------------------------------------------------------
3// Copyright © 2021, Douglas Creager.
4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6// ------------------------------------------------------------------------------------------------
7
8//! This crate provides a replacement for the standard [`Option`][] type where you have full
9//! control over how the `None` and `Some` variants are represented in memory.
10//!
11//! Normally, you don't have to think about this.  The standard [`Option`][] is a perfectly normal
12//! `enum`, and the compiler takes care of determining the most efficient in-memory representation.
13//! In particular, the compiler knows that certain types have [_niches_][] — in-memory bit patterns
14//! that do not represent valid values of the type.  If a type has a niche, then the compiler can
15//! use that bit pattern to represent the `None` variant.  This works automatically for most of the
16//! types you might care about: in particular, for references and the various `NonZero` types in
17//! `std::num`.
18//!
19//! However, sometimes a type has _multiple_ possible niches, and you need control over which one
20//! the compiler chooses to use.  Or, you might have defined a type such that the compiler cannot
21//! see that it has a niche available to use.  In this case, you can use the `Niche` and
22//! `ControlledOption` types from this crate to take full control over how the `None` and `Some`
23//! variants are laid out in memory.
24//!
25//! [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
26//! [_niches_]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche
27
28use std::alloc::Layout;
29
30/// A type should implement `Niche` if its memory representation has any bit patterns that do not
31/// represent valid values.  If so, one of those can be used to represent the `None` case of an
32/// option.
33pub trait Niche: Sized {
34    /// The type that is used to store values of `Self` inside of a `ControlledOption`.  This might
35    /// be `Self` itself, if your niche is a valid instance of the type, but which violates some
36    /// runtime constraint.  But if you cannot easily create your niche as an instance of `Self`,
37    /// you can use some other type, you can use some other type instead.
38    ///
39    /// A word of caution: is it this `Output` type that is stored inside of a `ControlledOption`.
40    /// If you want `ControlledOption<Self>` to have the same memory layout as `Self` (so that you
41    /// can use `#[repr(transparent)]`, for instance), then you must ensure that `Self` and
42    /// `Output` have the same layout, as determined by [`std::alloc::Layout::new`][new], and that
43    /// every valid bit pattern for `Self` is be a valid bit pattern for `Output` that returns
44    /// `true` for `is_some`.
45    ///
46    /// [new]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.new
47    type Output;
48
49    /// Returns the niche value for this type that should be used to represent `None` for a
50    /// `ControlledOption`.
51    fn none() -> Self::Output;
52
53    /// Returns whether value is the niche value for this type.
54    fn is_none(value: &Self::Output) -> bool;
55
56    /// Transforms a non-niche value of this type into its `Output` type.  When `Output` is `Self`,
57    /// this will be the identity function.
58    fn into_some(value: Self) -> Self::Output;
59
60    /// Transforms a non-niche value of this type from its `Output` type.  When `Output` is `Self`,
61    /// this will be the identity function.
62    fn from_some(value: Self::Output) -> Self;
63}
64
65/// An `Option` type where you have control over the in-memory representation of the `None` and
66/// `Some` variants.  See the [module-level documentation][parent] for more information.
67///
68/// [parent]: index.html
69#[repr(transparent)]
70pub struct ControlledOption<T>
71where
72    T: Niche,
73{
74    value: T::Output,
75}
76
77impl<T> ControlledOption<T>
78where
79    T: Niche,
80{
81    /// Creates a new `None` instance for this option.
82    #[inline]
83    pub fn none() -> ControlledOption<T> {
84        let value = T::none();
85        debug_assert!(T::is_none(&value));
86        ControlledOption { value }
87    }
88
89    /// Creates a new `Some` instance for this option.
90    #[inline]
91    pub fn some(value: T) -> ControlledOption<T> {
92        let value = T::into_some(value);
93        debug_assert!(!T::is_none(&value));
94        ControlledOption { value }
95    }
96
97    /// Returns `true` is the option is a `None` value.
98    #[inline]
99    pub fn is_none(&self) -> bool {
100        T::is_none(&self.value)
101    }
102
103    /// Returns `true` is the option is a `Some` value.
104    #[inline]
105    pub fn is_some(&self) -> bool {
106        !T::is_none(&self.value)
107    }
108
109    /// Transforms an [`Option`][] into a `ControlledOption`.
110    ///
111    /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
112    #[inline]
113    pub fn from_option(value: Option<T>) -> ControlledOption<T> {
114        value.into()
115    }
116
117    /// Transforms a `ControlledOption` into an [`Option`][].  This gives you access to all of the
118    /// usual assortment of useful methods that you expect from an `Option`.
119    ///
120    /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
121    #[inline]
122    pub fn into_option(self) -> Option<T> {
123        self.into()
124    }
125}
126
127impl<T> Default for ControlledOption<T>
128where
129    T: Niche,
130{
131    #[inline]
132    fn default() -> ControlledOption<T> {
133        ControlledOption::none()
134    }
135}
136
137impl<T> From<T> for ControlledOption<T>
138where
139    T: Niche,
140{
141    #[inline]
142    fn from(value: T) -> ControlledOption<T> {
143        ControlledOption::some(value)
144    }
145}
146
147impl<T> From<Option<T>> for ControlledOption<T>
148where
149    T: Niche,
150{
151    #[inline]
152    fn from(value: Option<T>) -> ControlledOption<T> {
153        match value {
154            Some(value) => ControlledOption::some(value),
155            None => ControlledOption::none(),
156        }
157    }
158}
159
160impl<T> Into<Option<T>> for ControlledOption<T>
161where
162    T: Niche,
163{
164    #[inline]
165    fn into(self) -> Option<T> {
166        if T::is_none(&self.value) {
167            None
168        } else {
169            Some(T::from_some(self.value))
170        }
171    }
172}
173
174// Normally we would #[derive] all of these traits, but the auto-derived implementations all
175// require that T implement the trait as well.  In our case, we (usually) need T::Output to
176// implement the traits, not T itself.
177
178impl<T> Clone for ControlledOption<T>
179where
180    T: Niche,
181    T::Output: Clone,
182{
183    fn clone(&self) -> Self {
184        ControlledOption {
185            value: self.value.clone(),
186        }
187    }
188}
189
190impl<T> Copy for ControlledOption<T>
191where
192    T: Niche,
193    T::Output: Copy,
194{
195}
196
197impl<T> std::fmt::Debug for ControlledOption<T>
198where
199    T: std::fmt::Debug + Niche,
200    T::Output: Clone,
201{
202    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
203        if self.is_none() {
204            write!(f, "ControlledOption::None")
205        } else {
206            f.debug_tuple("ControlledOption::Some")
207                .field(&T::from_some(self.value.clone()))
208                .finish()
209        }
210    }
211}
212
213impl<T> PartialEq for ControlledOption<T>
214where
215    T: Niche,
216    T::Output: PartialEq,
217{
218    fn eq(&self, other: &Self) -> bool {
219        self.value.eq(&other.value)
220    }
221
222    fn ne(&self, other: &Self) -> bool {
223        self.value.ne(&other.value)
224    }
225}
226
227impl<T> Eq for ControlledOption<T>
228where
229    T: Niche,
230    T::Output: Eq,
231{
232}
233
234impl<T> PartialOrd for ControlledOption<T>
235where
236    T: Niche,
237    T::Output: PartialOrd,
238{
239    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
240        self.value.partial_cmp(&other.value)
241    }
242
243    fn lt(&self, other: &Self) -> bool {
244        self.value.lt(&other.value)
245    }
246
247    fn le(&self, other: &Self) -> bool {
248        self.value.le(&other.value)
249    }
250
251    fn gt(&self, other: &Self) -> bool {
252        self.value.gt(&other.value)
253    }
254
255    fn ge(&self, other: &Self) -> bool {
256        self.value.ge(&other.value)
257    }
258}
259
260impl<T> Ord for ControlledOption<T>
261where
262    T: Niche,
263    T::Output: Ord,
264{
265    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
266        self.value.cmp(&other.value)
267    }
268
269    fn max(self, other: Self) -> Self {
270        ControlledOption {
271            value: self.value.max(other.value),
272        }
273    }
274
275    fn min(self, other: Self) -> Self {
276        ControlledOption {
277            value: self.value.min(other.value),
278        }
279    }
280
281    fn clamp(self, min: Self, max: Self) -> Self {
282        ControlledOption {
283            value: self.value.clamp(min.value, max.value),
284        }
285    }
286}
287
288impl<T> std::hash::Hash for ControlledOption<T>
289where
290    T: Niche,
291    T::Output: std::hash::Hash,
292{
293    fn hash<H>(&self, state: &mut H)
294    where
295        H: std::hash::Hasher,
296    {
297        self.value.hash(state)
298    }
299}
300
301//-------------------------------------------------------------------------------------------------
302// Structs
303//
304// The ‘controlled-option-macros’ crate provides a derive macro for the ‘Niche’ trait.  The derived
305// implementation depends on the following functions to get access to the field that you want to
306// use as the struct's niche.
307
308/// Automatically derives a [`Niche`][] implementation for a struct type.
309///
310/// You must mark one of the fields with a `#[niche]` attribute.  This field's type must already
311/// implement [`Niche`][].  The `None` value for the struct will be uninitialized memory, except
312/// for the chosen field, which will be filled in with its `None` niche value.  (This requires that
313/// the [`Niche`][] implementation for the field's type must have the same layout for its `Self`
314/// and `Output` types.)
315pub use controlled_option_macros::Niche;
316
317#[doc(hidden)]
318pub fn fill_struct_field_with_none<T>(field: *mut T)
319where
320    T: Niche,
321{
322    debug_assert!(Layout::new::<T>() == Layout::new::<T::Output>());
323    let repr = field as *mut T::Output;
324    unsafe { repr.write(T::none()) };
325}
326
327#[doc(hidden)]
328pub fn struct_field_is_none<T>(field: *const T) -> bool
329where
330    T: Niche,
331{
332    debug_assert!(Layout::new::<T>() == Layout::new::<T::Output>());
333    let repr = field as *const T::Output;
334    T::is_none(unsafe { &*repr })
335}
336
337//-------------------------------------------------------------------------------------------------
338// References
339
340impl<'a, T> Niche for &'a T {
341    type Output = *const T;
342
343    #[inline]
344    fn none() -> Self::Output {
345        std::ptr::null()
346    }
347
348    #[inline]
349    fn is_none(value: &Self::Output) -> bool {
350        value.is_null()
351    }
352
353    #[inline]
354    fn into_some(value: Self) -> Self::Output {
355        value
356    }
357
358    #[inline]
359    fn from_some(value: Self::Output) -> Self {
360        unsafe { &*value }
361    }
362}
363
364impl<'a, T> Niche for &'a mut T {
365    type Output = *mut T;
366
367    #[inline]
368    fn none() -> Self::Output {
369        std::ptr::null_mut()
370    }
371
372    #[inline]
373    fn is_none(value: &Self::Output) -> bool {
374        value.is_null()
375    }
376
377    #[inline]
378    fn into_some(value: Self) -> Self::Output {
379        value
380    }
381
382    #[inline]
383    fn from_some(value: Self::Output) -> Self {
384        unsafe { &mut *value }
385    }
386}
387
388//-------------------------------------------------------------------------------------------------
389// Non-zero types
390
391impl Niche for std::num::NonZeroI8 {
392    type Output = i8;
393
394    #[inline]
395    fn none() -> Self::Output {
396        0
397    }
398
399    #[inline]
400    fn is_none(value: &Self::Output) -> bool {
401        *value == 0
402    }
403
404    #[inline]
405    fn into_some(value: Self) -> Self::Output {
406        value.get()
407    }
408
409    #[inline]
410    fn from_some(value: Self::Output) -> Self {
411        unsafe { Self::new_unchecked(value) }
412    }
413}
414
415impl Niche for std::num::NonZeroI16 {
416    type Output = i16;
417
418    #[inline]
419    fn none() -> Self::Output {
420        0
421    }
422
423    #[inline]
424    fn is_none(value: &Self::Output) -> bool {
425        *value == 0
426    }
427
428    #[inline]
429    fn into_some(value: Self) -> Self::Output {
430        value.get()
431    }
432
433    #[inline]
434    fn from_some(value: Self::Output) -> Self {
435        unsafe { Self::new_unchecked(value) }
436    }
437}
438
439impl Niche for std::num::NonZeroI32 {
440    type Output = i32;
441
442    #[inline]
443    fn none() -> Self::Output {
444        0
445    }
446
447    #[inline]
448    fn is_none(value: &Self::Output) -> bool {
449        *value == 0
450    }
451
452    #[inline]
453    fn into_some(value: Self) -> Self::Output {
454        value.get()
455    }
456
457    #[inline]
458    fn from_some(value: Self::Output) -> Self {
459        unsafe { Self::new_unchecked(value) }
460    }
461}
462
463impl Niche for std::num::NonZeroI64 {
464    type Output = i64;
465
466    #[inline]
467    fn none() -> Self::Output {
468        0
469    }
470
471    #[inline]
472    fn is_none(value: &Self::Output) -> bool {
473        *value == 0
474    }
475
476    #[inline]
477    fn into_some(value: Self) -> Self::Output {
478        value.get()
479    }
480
481    #[inline]
482    fn from_some(value: Self::Output) -> Self {
483        unsafe { Self::new_unchecked(value) }
484    }
485}
486
487impl Niche for std::num::NonZeroIsize {
488    type Output = isize;
489
490    #[inline]
491    fn none() -> Self::Output {
492        0
493    }
494
495    #[inline]
496    fn is_none(value: &Self::Output) -> bool {
497        *value == 0
498    }
499
500    #[inline]
501    fn into_some(value: Self) -> Self::Output {
502        value.get()
503    }
504
505    #[inline]
506    fn from_some(value: Self::Output) -> Self {
507        unsafe { Self::new_unchecked(value) }
508    }
509}
510
511impl Niche for std::num::NonZeroU8 {
512    type Output = u8;
513
514    #[inline]
515    fn none() -> Self::Output {
516        0
517    }
518
519    #[inline]
520    fn is_none(value: &Self::Output) -> bool {
521        *value == 0
522    }
523
524    #[inline]
525    fn into_some(value: Self) -> Self::Output {
526        value.get()
527    }
528
529    #[inline]
530    fn from_some(value: Self::Output) -> Self {
531        unsafe { Self::new_unchecked(value) }
532    }
533}
534
535impl Niche for std::num::NonZeroU16 {
536    type Output = u16;
537
538    #[inline]
539    fn none() -> Self::Output {
540        0
541    }
542
543    #[inline]
544    fn is_none(value: &Self::Output) -> bool {
545        *value == 0
546    }
547
548    #[inline]
549    fn into_some(value: Self) -> Self::Output {
550        value.get()
551    }
552
553    #[inline]
554    fn from_some(value: Self::Output) -> Self {
555        unsafe { Self::new_unchecked(value) }
556    }
557}
558
559impl Niche for std::num::NonZeroU32 {
560    type Output = u32;
561
562    #[inline]
563    fn none() -> Self::Output {
564        0
565    }
566
567    #[inline]
568    fn is_none(value: &Self::Output) -> bool {
569        *value == 0
570    }
571
572    #[inline]
573    fn into_some(value: Self) -> Self::Output {
574        value.get()
575    }
576
577    #[inline]
578    fn from_some(value: Self::Output) -> Self {
579        unsafe { Self::new_unchecked(value) }
580    }
581}
582
583impl Niche for std::num::NonZeroU64 {
584    type Output = u64;
585
586    #[inline]
587    fn none() -> Self::Output {
588        0
589    }
590
591    #[inline]
592    fn is_none(value: &Self::Output) -> bool {
593        *value == 0
594    }
595
596    #[inline]
597    fn into_some(value: Self) -> Self::Output {
598        value.get()
599    }
600
601    #[inline]
602    fn from_some(value: Self::Output) -> Self {
603        unsafe { Self::new_unchecked(value) }
604    }
605}
606
607impl Niche for std::num::NonZeroUsize {
608    type Output = usize;
609
610    #[inline]
611    fn none() -> Self::Output {
612        0
613    }
614
615    #[inline]
616    fn is_none(value: &Self::Output) -> bool {
617        *value == 0
618    }
619
620    #[inline]
621    fn into_some(value: Self) -> Self::Output {
622        value.get()
623    }
624
625    #[inline]
626    fn from_some(value: Self::Output) -> Self {
627        unsafe { Self::new_unchecked(value) }
628    }
629}