const_exhaustive/
lib.rs

1#![cfg_attr(
2    any(docsrs, docsrs_dep),
3    feature(rustdoc_internals),
4    expect(internal_features, reason = "for `#[doc(fake_variadic)]`")
5)]
6#![doc = include_str!("../README.md")]
7#![no_std]
8
9mod array;
10
11use {
12    array::{concat, from_fn, map},
13    const_default::ConstDefault,
14    core::{
15        cell::UnsafeCell,
16        convert::Infallible,
17        marker::{PhantomData, PhantomPinned},
18        mem::MaybeUninit,
19        ops::{Add, Mul},
20    },
21    generic_array::{ArrayLength, GenericArray},
22    typenum::{Const, Pow, Sum, ToUInt, U, U0, U1, U2, Unsigned},
23    variadics_please::all_tuples,
24};
25pub use {
26    const_exhaustive_derive::Exhaustive,
27    generic_array::{self, const_transmute},
28    typenum,
29};
30
31/// All values of this type are known at compile time.
32///
33/// This trait should be derived instead of implemented manually - see
34/// [`const_exhaustive_derive::Exhaustive`].
35///
36/// If a type implements this trait, it guarantees that there is a finite set
37/// of possible values which may exist for this type, and that they can be
38/// enumerated at compile time. Due to this, an [`Exhaustive`] type must be
39/// [`Copy`], meaning that this type:
40/// - cannot have [`Drop`] logic
41/// - cannot store references or pointers
42///   - this rules out many complex types, including any heap-allocating types,
43///     such as strings
44///
45/// This trait is implemented for most types in `core` for which it makes sense.
46/// However, it is not implemented for any numerical types. Although there are
47/// practically a finite set of numbers for any given type (because they have to
48/// fit in a finite number of bits, e.g. a [`u8`] must fit in 8 bits), there are
49/// theoretically an infinite number of numbers, which goes against the spirit
50/// of this trait.
51///
52/// However, you may still want to define an exhaustive integer, where values
53/// may only be in a specific range e.g. `0..4`. In this case, you can either:
54/// - define an enum with each value explicitly
55/// - write a wrapper type which ensures that the value within it is always in
56///   range, then `unsafe impl Exhaustive` on the wrapper
57///
58/// # Safety
59///
60/// All possible values of this type, as representable in memory, must be
61/// present in [`Exhaustive::ALL`].
62///
63/// Example of implementing [`Exhaustive`] manually:
64///
65/// ```
66/// use const_exhaustive::{Exhaustive, generic_array::GenericArray, typenum};
67///
68/// // if you were implementing this for real,
69/// // you should probably use `NonZero<u8>` or `nonmax::NonMaxU8` for niche optimization;
70/// // but this is a simplified example
71/// #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
72/// struct UintUpTo4(u8);
73///
74/// impl UintUpTo4 {
75///     #[must_use]
76///     pub const fn new(n: u8) -> Option<Self> {
77///         if n < 4 { Some(Self(n)) } else { None }
78///     }
79///
80///     #[must_use]
81///     pub const fn get(self) -> u8 {
82///         self.0
83///     }
84/// }
85///
86/// unsafe impl Exhaustive for UintUpTo4 {
87///     type Num = typenum::U4;
88///
89///     const ALL: GenericArray<Self, Self::Num> =
90///         GenericArray::from_array([Self(0), Self(1), Self(2), Self(3)]);
91/// }
92///
93/// assert_eq!(
94///     [
95///         None,
96///         Some(UintUpTo4(0)),
97///         Some(UintUpTo4(1)),
98///         Some(UintUpTo4(2)),
99///         Some(UintUpTo4(3)),
100///     ],
101///     Option::<UintUpTo4>::ALL.as_slice()
102/// );
103/// ```
104#[diagnostic::on_unimplemented(
105    message = "all values of `{Self}` are not known statically",
106    label = "not exhaustive",
107    note = "consider annotating `{Self}` with `#[derive(Exhaustive)]`"
108)]
109pub unsafe trait Exhaustive: Sized + Copy {
110    /// Number of values that may exist of this type.
111    ///
112    /// Use [`typenum::Unsigned`] to get an actual [`usize`] out of this
113    /// type.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use const_exhaustive::{Exhaustive, typenum::Unsigned};
119    ///
120    /// assert_eq!(1, <() as Exhaustive>::Num::USIZE);
121    /// assert_eq!(2, <bool as Exhaustive>::Num::USIZE);
122    /// ```
123    type Num: ArrayLength<ArrayType<Self>: Copy>;
124
125    /// All values of this type.
126    ///
127    /// # Order
128    ///
129    /// Values in this array are guaranteed to be in a stable order. The
130    /// ordering of values is part of the public interface of this trait, so if
131    /// an implementation changes the ordering between versions, this is
132    /// considered a breaking change.
133    ///
134    /// Values should be ordered in a "binary counting" way, if it makes sense
135    /// on this type. Some examples of this ordering are outlined below. All
136    /// first-party implementations of [`Exhaustive`] follow this ordering.
137    ///
138    /// ## Primitives
139    ///
140    /// - [`Infallible`] has no values
141    /// - [`()`]: `[ () ]`
142    /// - [`bool`]: `[ false, true ]`
143    ///
144    /// ## Tuples
145    ///
146    /// ```
147    /// # use const_exhaustive::Exhaustive;
148    /// // in the same way that you count up in binary in this order:
149    /// //   00, 01, 10, 11
150    /// // we use a similar order for tuples
151    ///
152    /// assert_eq!(
153    ///     [(false, false), (false, true), (true, false), (true, true)],
154    ///     <(bool, bool)>::ALL.as_slice(),
155    /// );
156    /// ```
157    ///
158    /// ## Arrays
159    ///
160    /// ```
161    /// # use const_exhaustive::Exhaustive;
162    /// // `[T; N]` follows the same ordering as a tuple of `T`s with arity `N`:
163    /// // - `[T; 2]` has the same ordering as `(T, T)`
164    /// // - `[T; 3]` has the same ordering as `(T, T, T)`
165    ///
166    /// assert_eq!(
167    ///     [[false, false], [false, true], [true, false], [true, true]],
168    ///     <[bool; 2]>::ALL.as_slice(),
169    /// );
170    /// ```
171    ///
172    /// ## Derived on structs
173    ///
174    /// ```
175    /// # use const_exhaustive::Exhaustive;
176    /// // this has the exact same ordering as tuples
177    /// #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
178    /// struct MyStruct(bool, bool);
179    ///
180    /// // this also has the same ordering:
181    /// // struct MyStruct { a: bool, b: bool }
182    ///
183    /// assert_eq!(
184    ///     [
185    ///         MyStruct(false, false),
186    ///         MyStruct(false, true),
187    ///         MyStruct(true, false),
188    ///         MyStruct(true, true),
189    ///     ],
190    ///     MyStruct::ALL.as_slice()
191    /// );
192    /// ```
193    ///
194    /// ## Derived on enums
195    ///
196    /// ```
197    /// # use const_exhaustive::Exhaustive;
198    /// #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
199    /// enum MyEnum {
200    ///     A,
201    ///     B(bool),
202    ///     C,
203    /// }
204    ///
205    /// assert_eq!(
206    ///     [MyEnum::A, MyEnum::B(false), MyEnum::B(true), MyEnum::C],
207    ///     MyEnum::ALL.as_slice()
208    /// );
209    /// ```
210    const ALL: GenericArray<Self, Self::Num>;
211}
212
213unsafe impl Exhaustive for Infallible {
214    type Num = U0;
215
216    const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([]);
217}
218
219unsafe impl Exhaustive for () {
220    type Num = U1;
221
222    const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([()]);
223}
224
225unsafe impl Exhaustive for PhantomPinned {
226    type Num = U1;
227
228    const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([Self]);
229}
230
231unsafe impl<T: ?Sized> Exhaustive for PhantomData<T> {
232    type Num = U1;
233
234    const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([Self]);
235}
236
237unsafe impl Exhaustive for bool {
238    type Num = U2;
239
240    const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([false, true]);
241}
242
243unsafe impl<T: Exhaustive> Exhaustive for Option<T>
244where
245    U1: Add<T::Num, Output: ArrayLength<ArrayType<Self>: Copy>>,
246{
247    type Num = Sum<U1, T::Num>;
248
249    const ALL: GenericArray<Self, Self::Num> =
250        concat::<_, U1, T::Num>(GenericArray::from_array([None]), map!(T::ALL, |t| Some(t)));
251}
252
253unsafe impl<T: Exhaustive, E: Exhaustive> Exhaustive for Result<T, E>
254where
255    T::Num: Add<E::Num, Output: ArrayLength<ArrayType<Self>: Copy>>,
256{
257    type Num = Sum<T::Num, E::Num>;
258
259    const ALL: GenericArray<Self, Self::Num> = concat::<_, T::Num, E::Num>(
260        map!(T::ALL, |t| Ok::<T, E>(t)),
261        map!(E::ALL, |t| Err::<T, E>(t)),
262    );
263}
264
265unsafe impl<T: Exhaustive, const N: usize> Exhaustive for [T; N]
266where
267    Const<N>: ToUInt<Output: ArrayLength>,
268    <T::Num as ArrayLength>::ArrayType<usize>: ConstDefault,
269    T::Num: Pow<U<N>, Output: ArrayLength<ArrayType<Self>: Copy>>,
270{
271    type Num = <T::Num as Pow<U<N>>>::Output;
272
273    const ALL: GenericArray<Self, Self::Num> = from_fn!(Self::Num, |i| {
274        let perm: GenericArray<T, U<N>> = from_fn!(U<N>, |j| {
275            #[expect(
276                clippy::cast_possible_truncation,
277                reason = "we have no other way to cast in a const context"
278            )]
279            let index = (i / T::Num::USIZE.pow(N as u32 - j as u32 - 1)) % T::Num::USIZE;
280            T::ALL.as_slice()[index]
281        });
282        perm
283    });
284}
285
286// based on:
287// https://discord.com/channels/273534239310479360/1120124565591425034/1288250308958486579
288// https://discord.com/channels/273534239310479360/1120124565591425034/1288260177652617238
289// https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=3932fdb89b5b8f4e757cb62b43023e01
290
291type ProdAll<T> = <T as MulAll>::Output;
292
293// must be `pub` since it is used in `Exhaustive::Num`
294#[doc(hidden)]
295pub trait MulAll {
296    type Output: ArrayLength;
297}
298
299impl MulAll for () {
300    type Output = U1;
301}
302
303impl<T: ArrayLength> MulAll for (T,) {
304    type Output = T;
305}
306
307macro_rules! impl_variadic {
308    ($(#[$meta:meta])* $(($T:ident, $t:ident)),*) => {
309        impl<$($T,)* Last> MulAll for ($($T,)* Last,)
310        where
311            ($($T,)*): MulAll,
312            Last: Mul<<($($T,)*) as MulAll>::Output, Output: ArrayLength>,
313        {
314            type Output = <Last as Mul<<($($T,)*) as MulAll>::Output>>::Output;
315        }
316
317        $(#[$meta])*
318        unsafe impl<$($T: Exhaustive,)*> Exhaustive for ($($T,)*)
319        where
320            ($($T::Num,)*): MulAll,
321            <ProdAll<($($T::Num,)*)> as ArrayLength>::ArrayType<Self>: Copy,
322        {
323            type Num = ProdAll<($($T::Num,)*)>;
324
325            const ALL: GenericArray<Self, Self::Num> = {
326                let all: GenericArray<UnsafeCell<MaybeUninit<Self>>, Self::Num> =
327                    unsafe { MaybeUninit::uninit().assume_init() };
328
329                let mut i = 0;
330                while i < <ProdAll<($($T::Num,)*)>>::USIZE {
331                    let [$($t,)*] = split_index(i, [$($T::Num::USIZE,)*]);
332                    let tuple = ($($T::ALL.as_slice()[$t],)*);
333                    unsafe {
334                        *all.as_slice()[i].get() = MaybeUninit::new(tuple);
335                    }
336                    i += 1;
337                }
338
339                unsafe { const_transmute(all) }
340            };
341        }
342    };
343}
344
345all_tuples!(
346    #[doc(fake_variadic)]
347    impl_variadic,
348    1,
349    16,
350    T,
351    t
352);
353
354const fn split_index<const N: usize>(mut index: usize, lengths: [usize; N]) -> [usize; N] {
355    let mut result = [0; N];
356    let mut i = 0;
357    while i < N {
358        result[N - 1 - i] = index % lengths[N - 1 - i];
359        index /= lengths[N - 1 - i];
360        i += 1;
361    }
362    result
363}