Skip to content

get (new range iterator) breaks strum #944

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

Closed
joshka opened this issue May 21, 2024 · 2 comments
Closed

get (new range iterator) breaks strum #944

joshka opened this issue May 21, 2024 · 2 comments

Comments

@joshka
Copy link

joshka commented May 21, 2024

In ratatui/ratatui#1120 dependabot brought in itertools 0.13. We have a dependency on strum and use the EnumIter derive macro. This fails to compile with itertools due to adding the .get method on the iterator (which clashes with an existing method on the iterator created by the derive macro.)

E.g.:

use itertools::Itertools;
use strum::{Display, EnumIter, FromRepr, IntoEnumIterator};

#[derive(Debug, Clone, Copy, Default, Display, EnumIter, FromRepr, PartialEq, Eq)]
enum Tab {
    #[default]
    About,
    Recipe,
    Email,
    Traceroute,
    Weather,
}
Errors

error[E0277]: the trait bound `usize: IteratorIndex<&mut ConstraintNameIter>` is not satisfied
   --> examples/constraint-explorer.rs:53:54
    |
53  | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, EnumIter, FromRepr, Display)]
    |                                                      ^^^^^^^^ the trait `IteratorIndex<&mut ConstraintNameIter>` is not implemented for `usize`
    |
    = help: the following other types implement trait `IteratorIndex<I>`:
              RangeFull
              std::ops::Range<usize>
              RangeFrom<usize>
              RangeTo<usize>
              RangeInclusive<usize>
              RangeToInclusive<usize>
note: required by a bound in `itertools::Itertools::get`
   --> /Users/joshka/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.13.0/src/lib.rs:554:12
    |
551 |     fn get<R>(self, index: R) -> R::Output
    |        --- required by a bound in this associated function
...
554 |         R: traits::IteratorIndex<Self>,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Itertools::get`
    = note: this error originates in the derive macro `EnumIter` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `usize: IteratorIndex<&mut TabIter>` is not satisfied
   --> examples/demo2/app.rs:30:48
    |
30  | #[derive(Debug, Clone, Copy, Default, Display, EnumIter, FromRepr, PartialEq, Eq)]
    |                                                ^^^^^^^^ the trait `IteratorIndex<&mut TabIter>` is not implemented for `usize`
    |
    = help: the following other types implement trait `IteratorIndex<I>`:
              RangeFull
              std::ops::Range<usize>
              RangeFrom<usize>
              RangeTo<usize>
              RangeInclusive<usize>
              RangeToInclusive<usize>
note: required by a bound in `itertools::Itertools::get`
   --> /Users/joshka/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.13.0/src/lib.rs:554:12
    |
551 |     fn get<R>(self, index: R) -> R::Output
    |        --- required by a bound in this associated function
...
554 |         R: traits::IteratorIndex<Self>,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Itertools::get`
    = note: this error originates in the derive macro `EnumIter` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `ratatui` (example "constraint-explorer") due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
error: could not compile `ratatui` (example "demo2") due to 2 previous errors

EnumIter macro expansion

// Recursive expansion of EnumIter macro
// ======================================

#[doc = "An iterator over the variants of [Tab]"]
#[allow(missing_copy_implementations)]
struct TabIter {
    idx: usize,
    back_idx: usize,
    marker: ::core::marker::PhantomData<()>,
}
impl ::core::fmt::Debug for TabIter {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f.debug_struct("TabIter").field("len", &self.len()).finish()
    }
}
impl TabIter {
    fn get(&self, idx: usize) -> ::core::option::Option<Tab> {
        match idx {
            0usize => ::core::option::Option::Some(Tab::About),
            1usize => ::core::option::Option::Some(Tab::Recipe),
            2usize => ::core::option::Option::Some(Tab::Email),
            3usize => ::core::option::Option::Some(Tab::Traceroute),
            4usize => ::core::option::Option::Some(Tab::Weather),
            _ => ::core::option::Option::None,
        }
    }
}
impl ::strum::IntoEnumIterator for Tab {
    type Iterator = TabIter;
    fn iter() -> TabIter {
        TabIter {
            idx: 0,
            back_idx: 0,
            marker: ::core::marker::PhantomData,
        }
    }
}
impl Iterator for TabIter {
    type Item = Tab;
    fn next(&mut self) -> ::core::option::Option<<Self as Iterator>::Item> {
        self.nth(0)
    }
    fn size_hint(&self) -> (usize, ::core::option::Option<usize>) {
        let t = if self.idx + self.back_idx >= 5usize {
            0
        } else {
            5usize - self.idx - self.back_idx
        };
        (t, Some(t))
    }
    fn nth(&mut self, n: usize) -> ::core::option::Option<<Self as Iterator>::Item> {
        let idx = self.idx + n + 1;
        if idx + self.back_idx > 5usize {
            self.idx = 5usize;
            ::core::option::Option::None
        } else {
            self.idx = idx;
            self.get(idx - 1)
        }
    }
}
impl ExactSizeIterator for TabIter {
    fn len(&self) -> usize {
        self.size_hint().0
    }
}
impl DoubleEndedIterator for TabIter {
    fn next_back(&mut self) -> ::core::option::Option<<Self as Iterator>::Item> {
        let back_idx = self.back_idx + 1;
        if self.idx + back_idx > 5usize {
            self.back_idx = 5usize;
            ::core::option::Option::None
        } else {
            self.back_idx = back_idx;
            self.get(5usize - self.back_idx)
        }
    }
}
impl ::core::iter::FusedIterator for TabIter {}

impl Clone for TabIter {
    fn clone(&self) -> TabIter {
        TabIter {
            idx: self.idx,
            back_idx: self.back_idx,
            marker: self.marker.clone(),
        }
    }
}

I can work around this by just manually implementing the EnumIter trait from strum (it's an small shortcut), but I'd guess that this was unintentional. Naming a method get on a widely used extension trait intuitively seems like it might cause similar issues in many places. I'm not certain of this though, and it's possible I'm over-generalizing the failure mode from this one instance.

Caused by #891 and #447

@jswrenn
Copy link
Member

jswrenn commented May 21, 2024

This is a duplicate of #942, which is tracked by strum in Peternator7/strum#358, and will be fixed by Peternator7/strum#357.

@jswrenn jswrenn closed this as not planned Won't fix, can't repro, duplicate, stale May 21, 2024
@joshka
Copy link
Author

joshka commented May 21, 2024

Ah thanks - apologies for not searching.

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

No branches or pull requests

2 participants