`exhaust` v0.2.5 — iterate over every value of a type

exhaust is a small trait-and-derive-macro library providing the Exhaust trait, which allow iterating over all values of a given type (both std types and your own structs and enums via #[derive(Exhaust)]).

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) },
    ],
);

exhaust is useful for:

  • testing functions of enums and small structs with 100% coverage,
  • building lookup tables,
  • brute force searches,
  • and anything else where you want to compute or initialize something for every possible case specified by a type.

Today I released v0.2.5, which reduces code size and memory usage, and offers the factory_is_self option for deriving less code when possible. It’s not a flashy release with powerful new features, but I think that reflects that exhaust is a reliable library for its one purpose, so I thought it would be a good time to tell you about it for the first time. (Why not 1.0 yet?)

16 Likes

Neat crate!

I wonder about these exclusions:

u64, i64, and f64, because they are too large to feasibly exhaust.

... but you do implement tuples and arrays, and (u32, u32) and [u32; 2] have the same number of possibilities as u64. A similar argument can be made for u128 and i128 too.

No, I don't have a need for these -- it just seems inconsistent.

edit: now I see the other note: "length makes it feasible to actually exhaust [...] This may be infeasible to ensure in compositions;" indeed!

Indeed, there are many ways to have too many values to exhaust, and arguably, I should implement u64 and f64 just so that if someone does have a use for incomplete results, they aren’t stopped. Still, I think it has some value as a “think about what you are doing” obstacle.

I think, overall, if someone were to open an issue with an actual — not hypothetical — use case for impl Exhaust for u64, I’d happily add it, and change the documentation to say something more like “you should not expect these cases to be feasible”.

(It might even be good for the documentation to cover the general mathematical topic of just how easy it is to define something infeasibly large. Filed issue.)

2 Likes