Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Iterator trait bounds to IntoEnumIterator #314

Merged
merged 1 commit into from
Dec 3, 2023

Conversation

joshchngs
Copy link
Contributor

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

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())
}
@Peternator7 Peternator7 merged commit ffe8873 into Peternator7:master Dec 3, 2023
1 check passed
@Peternator7
Copy link
Owner

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants