Peternator7 / strum

A small rust library for adding custom derives to enums
https://crates.io/crates/strum
MIT License
1.8k stars 151 forks source link

Add Iterator trait bounds to IntoEnumIterator #314

Closed joshchngs closed 11 months ago

joshchngs commented 1 year ago

The concrete Iterator type for all implementations of IntoEnumIterator is guaranteed to implement these traits, because the only implementor is the #[derive(EnumIter)] macro, which emits an impl for each for the generated {EnumType}Iter struct.

However, when using IntoEnumIterator as a generic constraint, the concrete type is not known, so the impl of these traits cannot be inferred by the type checker with out additional help.

Here are some examples, using Itertools::cartesian_product() as the motivator, because it requires Iterator + Clone:

// We know this function will work, but it fails to type check
// without these additional trait bounds on IntoEnumIterator.
pub fn example_broken<T: Clone + IntoEnumIterator, U: Clone + IntoEnumIterator>(
) -> impl Iterator<Item = (T, U)> + Clone {
    T::iter().cartesian_product(U::iter())
}
// It's possible to add where constraints at the use point to
// workaround the issue, without this change.
// This version will typecheck.
pub fn example_workaround<T: Clone + IntoEnumIterator, U: Clone + IntoEnumIterator>(
) -> impl Iterator<Item = (T, U)>
where
    <T as IntoEnumIterator>::Iterator: Clone,
    <U as IntoEnumIterator>::Iterator: Clone,
{
    T::iter().cartesian_product(U::iter())
}

Tested against Rust 1.52.1, 1.72.1, & 1.74.0

Peternator7 commented 11 months ago

Thanks for the PR, this is technically a breaking change if someone had manually implemented EnumIter, but I generally don't maintain back compat in those scenarios so this feels like a good change to me. Makes it easier to use the associated type :)

Thanks!