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 .get(range) #891

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

Philippe-Cholet
Copy link
Member

@Philippe-Cholet Philippe-Cholet commented Mar 1, 2024

Closes #447.

So much changes happened since this work was initiated that rebase on top of master seems nothing less than a nightmare to me (I know, I tried). I therefore looked at the git differences, committed those (after formatting) and credited @wod3 and @TrolledWoods for their respective commits. Then I fixed my own review and a bit more.


I thought I would additionally implement IteratorIndex for Either<range, range> (and Box<dyn range> but I could not) but doing so would later result in a nasty breaking change if this was removed after an eventual inclusion to Iterator.

Details about either
    impl<L: Sealed, R: Sealed> Sealed for either::Either<L, R> {}


impl<I, L, R> IteratorIndex<I> for Either<L, R>
where
    I: Iterator,
    L: IteratorIndex<I>,
    R: IteratorIndex<I>,
{
    type Output = Either<<L as IteratorIndex<I>>::Output, <R as IteratorIndex<I>>::Output>;

    fn index(self, iter: I) -> Self::Output {
        // Could be more succinct with `map_either_with` but it would require to bump `either` version.
        match self {
            Either::Left(left) => Either::Left(left.index(iter)),
            Either::Right(right) => Either::Right(right.index(iter)),
        }
    }
}


    /// // It works nicely with either.
    /// use itertools::Either;
    ///
    /// let mut either_range = Either::Left(0..1);
    /// range = vec.iter().get(either_range).copied().collect();
    /// assert_eq!(&range, &[3]);
    ///
    /// either_range = Either::Right(2..);
    /// range = vec.iter().get(either_range).copied().collect();
    /// assert_eq!(&range, &[4, 1, 5]);

Copy link

codecov bot commented Mar 1, 2024

Codecov Report

Attention: Patch coverage is 96.36364% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 94.55%. Comparing base (6814180) to head (de08253).
Report is 64 commits behind head on master.

Files Patch % Lines
src/iter_index.rs 93.33% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #891      +/-   ##
==========================================
+ Coverage   94.38%   94.55%   +0.16%     
==========================================
  Files          48       49       +1     
  Lines        6665     6945     +280     
==========================================
+ Hits         6291     6567     +276     
- Misses        374      378       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

src/iter_index.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@scottmcm scottmcm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these are just things to ponder that might not need changing, but I think at least the overflow thing should be tweaked.

/// Returns an iterator over a subsection of the iterator.
///
/// See [`Itertools::get`] for more information.
pub fn get<I, R>(iter: I, index: R) -> R::Output
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very generic name to have as a top-level method. Does it really pull its weight?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name is appropriate — it 'gets' elements from the iterator. We use get for consistency with [T]::get, and match its API as closely as possible. There's little risk of conflict, since slices do not directly implement Iterator.

Copy link
Contributor

@scottmcm scottmcm May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Itertools trait, I understand.

But it's also pulled in as a function (https://github.com/rust-itertools/itertools/pull/891/files#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759R157). I guess it's unlikely to matter unless people are doing use itertools::*;, which is already discouraged, but as a thing not scoped to an impl it seems surprising.

(But you can feel free to "won't fix" this -- I don't feel that strongly about it.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah fair enough, we don't really need to make the free function get public.

src/iter_index.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/iter_index.rs Outdated Show resolved Hide resolved
@Philippe-Cholet
Copy link
Member Author

Philippe-Cholet commented May 2, 2024

I rebased on master and added 4 commits:

  1. Is there a reason to prefer Take<Skip> over Skip<Take>? As Skip<Take> is simpler defined!
  2. I don't see how we should really handle a range that includes usize::MAX other than with panic.
  3. Clarified when the resulting iterator is ExactSizeIterator and/or DoubleEndedIterator.
  4. fix 2nd commit: .get(1..=usize::MAX) should be fine

And removed get as a free function.

Philippe-Cholet and others added 9 commits May 2, 2024 16:32
Co-Authored-By: wod3 <17980899+wod3@users.noreply.github.com>
Co-Authored-By: TrolledWoods <26680270+trolledwoods@users.noreply.github.com>
Fix my own review.
Update docs.
Fix a warning with `#[cfg(doc)]`.
Use public path `traits::IteratorIndex` instead of private path `iter_index::IteratorIndex`.
As we should, I checked it can't pass without `let _ =`.
`RangeInclusive::is_empty` (rust 1.47+) is in conflict with our MSRV (1.43.1) so we can't use it yet.
The unspecified behavior is documented in `Itertools::get` so we simply remove this.
Unless there is a reason to prefer `Take<Skip>` over `Skip<Take>`?!
@Philippe-Cholet Philippe-Cholet force-pushed the 447-get-range branch 2 times, most recently from 3f53078 to 6477ac0 Compare May 3, 2024 05:52
A `use itertools::*;` might conflict with such a general function name.
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

3 participants