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}