Skip to main content

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