Skip to content

feat(Itertools): add strip_prefix and strip_prefix_by methods#1104

Merged
phimuemue merged 2 commits into
rust-itertools:masterfrom
SAY-5:feat/strip-prefix
May 21, 2026
Merged

feat(Itertools): add strip_prefix and strip_prefix_by methods#1104
phimuemue merged 2 commits into
rust-itertools:masterfrom
SAY-5:feat/strip-prefix

Conversation

@SAY-5
Copy link
Copy Markdown
Contributor

@SAY-5 SAY-5 commented May 18, 2026

Adds strip_prefix and strip_prefix_by to the Itertools trait, the iterator analogue of str::strip_prefix, as discussed in #1096. strip_prefix is implemented in terms of strip_prefix_by, which takes an equality predicate so the prefix items may differ in type from Self::Item. Includes doctests and quickcheck tests that assert parity with str::strip_prefix.

Closes #1096

@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.98%. Comparing base (6814180) to head (794df03).
⚠️ Report is 205 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1104      +/-   ##
==========================================
- Coverage   94.38%   93.98%   -0.41%     
==========================================
  Files          48       52       +4     
  Lines        6665     6679      +14     
==========================================
- Hits         6291     6277      -14     
- Misses        374      402      +28     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@phimuemue
Copy link
Copy Markdown
Member

phimuemue commented May 19, 2026

Hi there, thanks for this.

Regarding the API: Currently this only returns Some(self_after_prefix_removal) on success, but the failure case may be equally interesting for callers.

Can we - instead of Option<Self> - return a Result<Self, (Self, Self::Item, J::Item, J)>, where the Ok case resembles the prefix-removed iterator, and the Err case holds all cheaply-obtainable components that may be of interest for the caller?

  • prefix-partially-removed iterator (Self)
  • mismatched items (Self::Item vs J::Item)
  • remaining prefix (J)

Returning a bespoke struct for the error case may be viable, too.

@SAY-5
Copy link
Copy Markdown
Contributor Author

SAY-5 commented May 19, 2026

Makes sense, the failure case is worth exposing. I'll switch the return type to Result<Self, (Self, Self::Item, J::Item, J)> so callers can recover the partially-consumed iterators and the mismatched items. Will push the reworked version shortly.

@SAY-5
Copy link
Copy Markdown
Contributor Author

SAY-5 commented May 19, 2026

One edge case before I push: when self is exhausted before the prefix is fully matched, there is no mismatched Self::Item to return. Would you prefer Err((Self, Option<Self::Item>, J::Item, J::IntoIter)) so that None signals self-exhaustion, or a small bespoke error struct with a clearer field name for that case?

@phimuemue
Copy link
Copy Markdown
Member

One edge case before I push: when self is exhausted before the prefix is fully matched, there is no mismatched Self::Item to return. Would you prefer Err((Self, Option<Self::Item>, J::Item, J::IntoIter)) so that None signals self-exhaustion, or a small bespoke error struct with a clearer field name for that case?

Good catch.

Maybe this makes things clear?

struct StripPrefixError {
 iterator: Self,
 prefix: J,
 mismatch: (Option<Self::Item>, J::Item)
}

Aside: Maybe even rename J to Prefix?

@SAY-5 SAY-5 force-pushed the feat/strip-prefix branch from 7a655bf to 64920b7 Compare May 20, 2026 23:13
@SAY-5
Copy link
Copy Markdown
Contributor Author

SAY-5 commented May 20, 2026

Reworked to use a public StripPrefixError { iterator, prefix, mismatch } struct (with Prefix typed parameter as you suggested). The Result<Self, _> now hands back the partially-consumed iterator and remaining prefix; mismatch.0 is None when self was exhausted before the prefix was fully matched. Doctest, quickcheck, and two regression unit tests covering both error shapes are green locally.

@SAY-5 SAY-5 force-pushed the feat/strip-prefix branch from 64920b7 to 51351be Compare May 21, 2026 07:03
Copy link
Copy Markdown
Member

@phimuemue phimuemue left a comment

Choose a reason for hiding this comment

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

Thank you for this. Could you adopt my changes, then I think we're good to go.

Comment thread src/lib.rs Outdated
///
/// All fields are public so callers can recover the partially-consumed
/// iterators and the mismatched items.
#[derive(Debug, Clone, PartialEq, Eq)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's avoid PartialEq, Eq if it's unclear if it is really needed. (Comparing iterator is oftentimes not doable via PartialEq oftentimes.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Dropped, kept only Debug, Clone.

Comment thread src/lib.rs Outdated
Comment on lines +5182 to +5195
let mut prefix = prefix.into_iter();
while let Some(wanted) = prefix.next() {
match self.next() {
Some(got) if eq(&got, &wanted) => continue,
got => {
return Err(StripPrefixError {
iterator: self,
prefix,
mismatch: (got, wanted),
});
}
}
}
Ok(self)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We canonically try to use avoid the while let Some(wanted) = prefix.next pattern. Can you change it to this?

        let mut prefix = prefix.into_iter();
        match prefix.by_ref().try_for_each(|wanted| {
            match self.next() {
                Some(got) if eq(&got, &wanted) => Ok(()),
                got => Err((got, wanted)),
            }
        }) {
            Ok(()) => Ok(self),
            Err(mismatch) => Err(StripPrefixError {
                iterator: self,
                prefix,
                mismatch,
            }),
        }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Switched to try_for_each per your snippet.

Copy link
Copy Markdown
Member

@phimuemue phimuemue left a comment

Choose a reason for hiding this comment

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

Thank you.

@phimuemue phimuemue added this pull request to the merge queue May 21, 2026
Merged via the queue into rust-itertools:master with commit d5897f7 May 21, 2026
14 checks passed
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.

Itertools::strip_prefix

2 participants