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}