pub trait Exhaust: Sized {
type Iter: FusedIterator<Item = Self::Factory> + Clone + Debug;
type Factory: Clone + Debug;
// Required methods
fn exhaust_factories() -> Self::Iter;
fn from_factory(factory: Self::Factory) -> Self;
// Provided method
fn exhaust() -> Iter<Self> ⓘ { ... }
}
Expand description
Types that can be exhaustively iterated. That is, an iterator is available which produces every possible value of this type.
§Properties
Implementations must have the following properties:
-
Exhaustiveness: If
Self: PartialEq
, then for every valuea
of typeSelf
, there is some elementb
ofSelf::exhaust()
for whicha == b
, unless it is the case thata != a
.If there is no
PartialEq
implementation, then follow the spirit of this rule anyway. -
No duplicates: if
Self: PartialEq
, then for any two itemsa, b
produced by the iterator,a != b
.If this rule comes into conflict with exhaustiveness, then exhaustiveness takes priority.
-
If there is any value
a
of typeSelf
for whicha != a
, thenExhaust
must produce one or more such values (e.g.f32::NAN
). -
The iterator has a finite length.
For example, collections which can contain arbitrary numbers of duplicate elements, like
Vec
, should not implementExhaust
, because they cannot have an iterator which is both finite and exhaustive. -
Purity/determinism: every call to
Self::exhaust()
, orClone::clone()
of a returned iterator or factory, should produce the same sequence of items.(If this is not upheld, then derived implementations of
Exhaust
on types containing this type will not behave consistently.) -
exhaust()
does not panic, nor does the iterator it returns, except in the event that memory allocation fails. -
All produced values should be valid according to
Self
’s invariants as enforced by its ordinary constructors. When the above properties refer to “a value of typeSelf
”, they do not include invalid values.
The following further properties are recommended when feasible:
-
If
Self: Ord
, then the items are sorted in ascending order. -
The iterator’s length makes it feasible to actually exhaust.
For example,
u64
does not implementExhaust
. This may be infeasible to ensure in compositions; e.g.[u16; 4]
is even more infeasible to exhaust thanu64
.
Exhaust
is not an unsafe trait
, and as such, no soundness property should rest
on implementations having any of the above properties unless the particular implementation
guarantees them.
§Examples
Using derive(Exhaust)
to implement the trait:
use exhaust::Exhaust;
#[derive(PartialEq, Debug, Exhaust)]
struct Foo {
a: bool,
b: Bar,
}
#[derive(PartialEq, Debug, Exhaust)]
enum Bar {
One,
Two(bool),
}
assert_eq!(
Foo::exhaust().collect::<Vec<Foo>>(),
vec![
Foo { a: false, b: Bar::One },
Foo { a: false, b: Bar::Two(false) },
Foo { a: false, b: Bar::Two(true) },
Foo { a: true, b: Bar::One },
Foo { a: true, b: Bar::Two(false) },
Foo { a: true, b: Bar::Two(true) },
],
);
Writing a manual implementation of Exhaust
:
use exhaust::Exhaust;
#[derive(Clone, Debug)]
struct AsciiLetter(char);
impl Exhaust for AsciiLetter {
type Iter = ExhaustAsciiLetter;
// We could avoid needing to `derive(Clone, Debug)` by using `char` as the factory,
// but if we did that, then `from_factory()` must check its argument for validity.
type Factory = Self;
fn exhaust_factories() -> Self::Iter {
ExhaustAsciiLetter { next: 'A' }
}
fn from_factory(factory: Self::Factory) -> Self {
factory
}
}
#[derive(Clone, Debug)] // All `Exhaust::Iter`s must implement `Clone` and `Debug`.
struct ExhaustAsciiLetter {
next: char
}
impl Iterator for ExhaustAsciiLetter {
type Item = AsciiLetter;
fn next(&mut self) -> Option<Self::Item> {
match self.next {
'A'..='Y' | 'a'..='z' => {
let item = self.next;
self.next = char::from_u32(self.next as u32 + 1).unwrap();
Some(AsciiLetter(item))
}
'Z' => {
self.next = 'a';
Some(AsciiLetter('Z'))
}
'{' => None, // ('z' + 1)
_ => unreachable!(),
}
}
}
impl std::iter::FusedIterator for ExhaustAsciiLetter {}
assert_eq!(
AsciiLetter::exhaust().map(|l| l.0).collect::<String>(),
String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
);
§Excluded Types
The following primitive or standard library types do not implement Exhaust
for
particular reasons:
-
References, because there’s nowhere to stash the referent. (This could be changed for small finite types, like
&bool
, but those are the same sort of types which are unlikely to be used by reference.) -
Pointers, for the same reason as references (and we could generate invalid pointers, but that would be almost certainly pointless).
-
u64
,i64
, andf64
, because they are too large to feasibly exhaust. -
Containers that permit duplicate items, and can therefore be unboundedly large:
-
core::mem::ManuallyDrop
, because it would be a memory leak. -
core::mem::MaybeUninit
, because it is not useful to obtain aMaybeUninit<T>
value without knowing whether it is initialized, and if they are to be all initialized, thenT::exhaust()
is just as good. -
core::ops::Range
andcore::ops::RangeInclusive
, because it is ambiguous whether inverted (start > end) ranges should be generated. -
std::io::ErrorKind
and other explicitly non-exhaustive types.
Required Associated Types§
Sourcetype Iter: FusedIterator<Item = Self::Factory> + Clone + Debug
type Iter: FusedIterator<Item = Self::Factory> + Clone + Debug
Iterator type returned by Self::exhaust_factories()
.
See the trait documentation for what properties this iterator should have.
Note: While it is necessary for this type to be exposed, an implementation of
Exhaust
changing to another iterator type should not be considered a breaking
change, as long as it still has the same iterator properties (e.g.
ExactSizeIterator
); it should be treated as an implementation detail.
Sourcetype Factory: Clone + Debug
type Factory: Clone + Debug
Data which can be used to construct Self
.
The difference between Self
and Self::Factory
is that the Factory
must
implement Clone
, while Self
is not required to.
This is relevant to, and motivated by, the following cases:
-
Types which do not implement
Clone
, or which conditionally implementClone
, can still implementExhaust
by providing aFactory
type which is notSelf
. For example, interior-mutable types often do not implementClone
, or implement it so as to make a new handle to existing shared state; those types should choose aFactory
type which represents their initial state only. -
Generic containers of two or more values need to generate all combinations of their values. The guarantee that the contents’
Factory
isClone
allows them to use clones of the factories to perform this iteration straightforwardly. (It would be theoretically possible to avoid this by cloning the exhausting iterators themselves, but much more complex and difficult to implement correctly.) For example,[AtomicBool; 2]
ends up using[bool; 2]
as its factory, which implementsClone
even thoughAtomicBool
does not. -
A lot of wrapper types can easily implement
Exhaust
by delegating to another iterator and merely implementingSelf::from_factory()
to add the wrapper. This is not more powerful than use ofIterator::map()
, but it is often more convenient.
Types which implement Clone
and are not generic can use type Factory = Self;
if they wish.
Note: While it is necessary for this type to be exposed, an implementation of
Exhaust
changing to another factory type should not be considered a breaking
change; it should be treated as an implementation detail, unless otherwise documented.
Required Methods§
Sourcefn exhaust_factories() -> Self::Iter
fn exhaust_factories() -> Self::Iter
Returns an iterator over factories for all values of this type.
Implement this function to implement the trait. Call this function when implementing an
Exhaust::Iter
iterator for a type that contains this type.
See the trait documentation for what properties this iterator should have.
Sourcefn from_factory(factory: Self::Factory) -> Self
fn from_factory(factory: Self::Factory) -> Self
Construct a concrete value of this type from a Self::Factory
value produced by
its Self::Iter
.
Caution: While this function is meant to be used only with values produced by the iterator, this cannot be enforced; therefore, make sure it cannot bypass any invariants that the type might have.
§Panics
- This function may panic if given a factory value that is not one of the values
Self::Iter
is able to produce. - This function may panic or abort if memory allocation that is required to construct
Self
fails.
Implementations should not panic under any other circumstances.
Provided Methods§
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.
Implementations on Foreign Types§
Source§impl Exhaust for Infallible
impl Exhaust for Infallible
type Iter = Empty<Infallible>
type Factory = Infallible
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl Exhaust for FpCategory
impl Exhaust for FpCategory
type Iter = IntoIter<FpCategory, { $array.len() }>
type Factory = FpCategory
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl Exhaust for RecvTimeoutError
impl Exhaust for RecvTimeoutError
type Iter = IntoIter<RecvTimeoutError, { $array.len() }>
type Factory = RecvTimeoutError
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl Exhaust for TryRecvError
impl Exhaust for TryRecvError
type Iter = IntoIter<TryRecvError, { $array.len() }>
type Factory = TryRecvError
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl Exhaust for f32
Note: The floats produced include many NaN
s (all unequal in representation).
impl Exhaust for f32
Note: The floats produced include many NaN
s (all unequal in representation).
Source§impl Exhaust for PhantomPinned
impl Exhaust for PhantomPinned
Source§impl Exhaust for AtomicBool
impl Exhaust for AtomicBool
Source§impl<H> Exhaust for BuildHasherDefault<H>
impl<H> Exhaust for BuildHasherDefault<H>
Source§impl<T0, T1, T2> Exhaust for (T0, T1, T2)
impl<T0, T1, T2> Exhaust for (T0, T1, T2)
Source§impl<T0, T1, T2, T3> Exhaust for (T0, T1, T2, T3)
impl<T0, T1, T2, T3> Exhaust for (T0, T1, T2, T3)
Source§impl<T0, T1, T2, T3, T4> Exhaust for (T0, T1, T2, T3, T4)
impl<T0, T1, T2, T3, T4> Exhaust for (T0, T1, T2, T3, T4)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5> Exhaust for (T0, T1, T2, T3, T4, T5)
impl<T0, T1, T2, T3, T4, T5> Exhaust for (T0, T1, T2, T3, T4, T5)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5, T6> Exhaust for (T0, T1, T2, T3, T4, T5, T6)
impl<T0, T1, T2, T3, T4, T5, T6> Exhaust for (T0, T1, T2, T3, T4, T5, T6)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5, T6>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory, <T6 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5, T6, T7> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7)
impl<T0, T1, T2, T3, T4, T5, T6, T7> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5, T6, T7>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory, <T6 as Exhaust>::Factory, <T7 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5, T6, T7, T8> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5, T6, T7, T8>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory, <T6 as Exhaust>::Factory, <T7 as Exhaust>::Factory, <T8 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory, <T6 as Exhaust>::Factory, <T7 as Exhaust>::Factory, <T8 as Exhaust>::Factory, <T9 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory, <T6 as Exhaust>::Factory, <T7 as Exhaust>::Factory, <T8 as Exhaust>::Factory, <T9 as Exhaust>::Factory, <T10 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Exhaust for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
type Iter = ExhaustTupleIter<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
type Factory = (<T0 as Exhaust>::Factory, <T1 as Exhaust>::Factory, <T2 as Exhaust>::Factory, <T3 as Exhaust>::Factory, <T4 as Exhaust>::Factory, <T5 as Exhaust>::Factory, <T6 as Exhaust>::Factory, <T7 as Exhaust>::Factory, <T8 as Exhaust>::Factory, <T9 as Exhaust>::Factory, <T10 as Exhaust>::Factory, <T11 as Exhaust>::Factory)
fn exhaust_factories() -> Self::Iter
fn from_factory(factory: Self::Factory) -> Self
Source§impl<T> Exhaust for UnsafeCell<T>where
T: Exhaust,
impl<T> Exhaust for UnsafeCell<T>where
T: Exhaust,
Source§impl<T> Exhaust for PhantomData<T>
impl<T> Exhaust for PhantomData<T>
Source§impl<T: Exhaust + AsRef<[u8]> + Clone + Debug> Exhaust for Cursor<T>
Produces each combination of a buffer state and a cursor position, except for those
where the position is beyond the end of the buffer.
impl<T: Exhaust + AsRef<[u8]> + Clone + Debug> Exhaust for Cursor<T>
Produces each combination of a buffer state and a cursor position, except for those where the position is beyond the end of the buffer.
Source§impl<T: Exhaust> Exhaust for TrySendError<T>
impl<T: Exhaust> Exhaust for TrySendError<T>
Source§impl<T: Exhaust> Exhaust for RangeToInclusive<T>
impl<T: Exhaust> Exhaust for RangeToInclusive<T>
Source§impl<T: ?Sized + ToOwned<Owned = O>, O: Exhaust> Exhaust for Cow<'_, T>
Note that this implementation necessarily ignores the borrowed versus owned distinction;
every value returned will be a Cow::Owned
, not a Cow::Borrowed
.
This agrees with the PartialEq
implementation for Cow
, which considers
owned and borrowed to be equal.
impl<T: ?Sized + ToOwned<Owned = O>, O: Exhaust> Exhaust for Cow<'_, T>
Note that this implementation necessarily ignores the borrowed versus owned distinction;
every value returned will be a Cow::Owned
, not a Cow::Borrowed
.
This agrees with the PartialEq
implementation for Cow
, which considers
owned and borrowed to be equal.