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

Merge multipeek peeknth #940

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

Owen-CH-Leung
Copy link
Contributor

As per #933 , this PR merges MultiPeek and PeekNth into a general private interface MultiPeekGeneral, with public type alias to define the compatible MultiPeek and PeekNth struct.

Copy link

codecov bot commented May 17, 2024

Codecov Report

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

Project coverage is 94.32%. Comparing base (6814180) to head (4fc8456).
Report is 94 commits behind head on master.

Files Patch % Lines
src/multipeek_general.rs 77.94% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #940      +/-   ##
==========================================
- Coverage   94.38%   94.32%   -0.07%     
==========================================
  Files          48       48              
  Lines        6665     7013     +348     
==========================================
+ Hits         6291     6615     +324     
- Misses        374      398      +24     

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

Copy link
Member

@Philippe-Cholet Philippe-Cholet left a comment

Choose a reason for hiding this comment

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

I reviewed but I'm not sure we should continue this, as merge implementations imply to merge method documentations, while the index behavior is different.
@phimuemue What do you think? Was I overzealous here?

src/lib.rs Outdated
Comment on lines 117 to 120
#[cfg(feature = "use_alloc")]
pub use crate::multipeek_impl::MultiPeek;
pub use crate::pad_tail::PadUsing;
pub use crate::multipeek_general::MultiPeek;
#[cfg(feature = "use_alloc")]
pub use crate::peek_nth::PeekNth;
pub use crate::multipeek_general::PeekNth;
Copy link
Member

Choose a reason for hiding this comment

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

You can merge those imports.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup - merged.

Comment on lines 244 to 246
impl<I: Iterator, Idx: PeekIndex> PeekingNext for MultiPeekGeneral<I, Idx>
where
I: Iterator,
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Iterator bound twice, move Idx bound below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Moved Idx bound below

where
I: Iterator,
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
self.peek().filter(|item| accept(item))?;
self.peek_mut().filter(|item| accept(item))?;
Copy link
Member

Choose a reason for hiding this comment

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

I don't see why it would now need peek_mut.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Reverted to peek

Comment on lines 196 to 218
pub fn peek(&mut self) -> Option<&I::Item> {
let ret = if self.index < self.buf.len() {
Some(&self.buf[self.index])
} else {
match self.iter.next() {
Some(x) => {
self.buf.push_back(x);
Some(&self.buf[self.index])
}
None => return None,
}
};

self.index += 1;
ret
}
}

impl<I: Iterator> PeekNth<I> {
/// Works exactly like the `peek` method in [`std::iter::Peekable`].
pub fn peek(&mut self) -> Option<&I::Item> {
self.peek_nth(0)
}
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 it could be merged into MultiPeekGeneral::peek? With a PeekIndex::increment_index.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes - I've merged peek into MultiPeekGeneral and removed the impl block

Comment on lines 26 to 27
/// [`IntoIterator`] enabled version of [`crate::Itertools::multipeek`].
pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter>
Copy link
Member

Choose a reason for hiding this comment

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

To avoid crate:: to be user-visible in doc, we usually do:

  • [`Itertools::multipeek`](crate::Itertools::multipeek) which I usually prefer
  • (OR #[cfg(doc)] use crate::Itertools; at the top of the file to be able to only write [`Itertools::multipeek`] in the doc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I've revised to use

[`Itertools::multipeek`](crate::Itertools::multipeek) 

Comment on lines 1 to 2
#![allow(private_interfaces)]
#![allow(private_bounds)]
Copy link
Member

Choose a reason for hiding this comment

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

I discover those lints, or rather I probably usually fix what those lints tell me rather than allow them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right. Turns out it passes the clippy even the lints are removed. I've removed them

src/free.rs Outdated
Comment on lines 22 to 25
#[cfg(feature = "use_alloc")]
pub use crate::multipeek_impl::multipeek;
pub use crate::multipeek_general::multipeek;
#[cfg(feature = "use_alloc")]
pub use crate::peek_nth::peek_nth;
pub use crate::multipeek_general::peek_nth;
Copy link
Member

Choose a reason for hiding this comment

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

You can merge those lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Merged

Comment on lines 12 to 14
pub iter: Fuse<I>,
pub buf: VecDeque<I::Item>,
pub index: Idx,
Copy link
Member

Choose a reason for hiding this comment

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

Almost missed this. The fields should remain private! If there is a need to access them from another module, you can pub(crate) the fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks - I've changed them to private fields

Comment on lines 60 to 62
impl PeekIndex for () {
fn reset_index(&mut self) {}
}
Copy link
Member

Choose a reason for hiding this comment

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

I mention elsewhere the need to add a fn increment_index(&mut self); but we probably need a more general fn add(&mut self, n: usize) { *self += n; } instead to update the index with the nth methods (and create tests about it).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I've revised increment_index to a generic add instead.

@@ -137,15 +165,64 @@ where
{
self.next_if(|next| next == expected)
}

/// Works exactly like `next_if`, but for the `nth` value without advancing the iterator.
pub fn nth_if(&mut self, n: usize, func: impl FnOnce(&I::Item) -> bool) -> Option<&I::Item> {
Copy link
Member

Choose a reason for hiding this comment

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

What should we expect from MultiPeek::nth_if concerning the index? To advance only if func returned true I guess. But I would expect the doc of the method to decribe this but we can't make different docs for MultiPeek/PeekNth without making different methods.
⚠ Maybe I was too optimistic about this merge. ⚠

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe with the merge we'd need to completely revise the doc to describe the API behaviour under MultiPeek and PeekNth. I can help to re-write them if we decide to go for the direction to merge both.

@phimuemue
Copy link
Member

My suggestion: Deprecate multipeek and unify all the functionality into peek_nth.

Rationale: peek_nth is (advertised as) a drop-in replacement for std::iter::Peekable (nice for users), whereas multipeek offers exactly the same interface, but has subtle semantic differences (not nice for users if they are bitten by this).

And imho we should even un-fuse it to really be in line with std::iter::Peekable.

@Philippe-Cholet
Copy link
Member

According to https://github.com/search?q=itertools+multipeek+reset_peek+lang%3ARust&type=code there is some real use of MultiPeek. Deprecate it seems excessive to me.

Maybe the status quo is not so bad.

I'm not opposed to unfuse it but it's a breaking change, I'd prefer to not break things for a while.

@phimuemue
Copy link
Member

phimuemue commented Jul 14, 2024

According to https://github.com/search?q=itertools+multipeek+reset_peek+lang%3ARust&type=code there is some real use of MultiPeek. Deprecate it seems excessive to me.
[...]
I'm not opposed to unfuse it but it's a breaking change, I'd prefer to not break things for a while.

Yes, we should not deprecate at a whim. But honestly I really like to know if deprecating wasn't for the better in the long run.

My rationale (and the linked examples appear to confirm this to some extent):

  • People use MultiPeek even when there are simpler, more efficient alternatives: In particular, cloneable iterators. These could just be cloned - often more efficient than an allocating MultiPeek must allocate. So these programs do not need MultiPeek in the first place.
    => Maybe depretacing it or offering another API pushes users towards the better alternatives.
  • Where it's really needed: MultiPeek requires the users to be careful with the "cursor", so that callers sprinkle reset_peak because they need their iterator in a predictable state. I'm not sure that there's a simple API that avoids the problem, but I find this a recurring (and bad) pattern with this iterator.
    => Is there any real use case that does not conclude peeking with reset_peek? If not, can we offer an API that does not put this burden onto the callers?
  • As said, subtle difference between PeekNth and MultiPeek.
    => If prefer an API that makes this distinction more explicit.

On top of that, I do not see a problem in keeping a deprecated MultiPeek and motivate users to go with the replacements (if we find them) in PeekNth.

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.

3 participants