-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Optimize strip_prefix and strip_suffix with str patterns #69784
Conversation
r? @kennytm (rust_highfive has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just asking some questions because I'm new to rust but want to learn and get more involved! :-)
@@ -3984,26 +3984,15 @@ impl str { | |||
/// ``` | |||
/// #![feature(str_strip)] | |||
/// | |||
/// assert_eq!("foobar".strip_prefix("foo"), Some("bar")); | |||
/// assert_eq!("foobar".strip_prefix("bar"), None); | |||
/// assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering - do these changes improve the test cases or are they simply to make the docs more readable? Or some other thing? I assume it's not because the previous tests failed with the new implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They improve the test cases to account for a bug that I introduced while rewriting these methods that was not properly caught by the old test cases. The old test cases had the defect that they split the string exactly in half. My buggy implementation for strip_suffix
sliced from s[pat.len()..]
when it needed to slice from s[s.len() - pat.len()..]
, which was not caught by any of the previous test cases.
/// assert_eq!("foobar".strip_prefix("foo"), Some("bar")); | ||
/// assert_eq!("foobar".strip_prefix("bar"), None); | ||
/// assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar")); | ||
/// assert_eq!("foo:bar".strip_prefix("bar"), None); | ||
/// assert_eq!("foofoo".strip_prefix("foo"), Some("foo")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be updated to foo:foo
? I guess this partly depends on the comment above :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent of this test case, as best as I can tell, is to check that strip_prefix(P)
only strips one prefix from a string like PPPP
, so adding the colon would change what this test is intended to do.
src/libcore/str/mod.rs
Outdated
@@ -3041,7 +3041,7 @@ impl str { | |||
/// ``` | |||
#[stable(feature = "rust1", since = "1.0.0")] | |||
pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { | |||
pat.is_prefix_of(self) | |||
pat.strip_prefix_from(self).is_some() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm missing something :-( - what is this change for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, it's errant. Thanks. It's from an earlier revision of the PR where I was experimenting with removing the is_prefix_of
and is_suffix_of
methods and rewriting them in terms of the new strip_prefix_from
and strip_suffix_from
methods.
src/liballoc/string.rs
Outdated
|
||
#[inline] | ||
fn strip_prefix_from(self, haystack: &'a str) -> Option<&'a str> { | ||
self[..].strip_prefix_from(haystack) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm having a little trouble following the flow of these methods. I'm going to botch some made up syntax and pretend Module.method
is the method
method on an instance of the Module
struct but can you tell me if this what's going on?
We have
String.strip_prefix_from
which callsstr.strip_prefix_from
which is an implementation ofPattern.strip_prefix_from
optimized because we know the length of theString
instancestr.strip_prefix
which callsprefix.strip_prefix_from(self)
which will call the optimized implementation ifprefix
is astr
orchar
char.strip_prefix_from
which is an optimized implementation ofPattern.strip_prefix_from
- a default implementation of
Pattern.strip_prefix_from
which is for ... I don't know 🤔 The PR description says "they have default implementations so the change is backwards compatible" - what does that mean? That sounds like, "if we no longer overridestrip_prefix_from
forstr
orchar
they'll get the default implementation"? Or am I reading that wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, exactly right! (4) is for anyone who wants to implement Pattern
for their own type (say, a function) but doesn't want to go to the trouble of implementing a specialized strip_prefix_from
or strip_suffix_from method
—or perhaps their pattern is not of a fixed length and therefore there isn't a way to optimize the method beyond constructing and using a full search.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh okay I understand now! I was originally confused because I didn't notice that, if there wasn't a default implementation, implementors would have to supply their own implementation. Forgot that optional methods without defaults aren't things :-) Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You bet!
ee53aa0
to
b00a211
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any "Resolve Conversation" button, but you resolved all my comments, thank you :-)
Just one more question and then this is ✅ from my perspective but I have no idea what I'm talking about so I'd rather someone else review this
src/libcore/str/pattern.rs
Outdated
@@ -47,6 +47,22 @@ pub trait Pattern<'a>: Sized { | |||
matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) | |||
} | |||
|
|||
/// Removes the pattern from the front of haystack, if it matches. | |||
#[inline] | |||
fn strip_prefix_from(self, haystack: &'a str) -> Option<&'a str> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this method no longer have tests? Do we want it to still have tests?
☔ The latest upstream changes (presumably #69986) made this pull request unmergeable. Please resolve the merge conflicts. |
@benesch You need to rebase this PR. |
0b86e69
to
dbbea25
Compare
Thanks, done. |
Constructing a Searcher in strip_prefix and strip_suffix is unnecessarily slow when the pattern is a fixed-length string. Add strip_prefix and strip_suffix methods to the Pattern trait, and add optimized implementations of these methods in the str implementation. The old implementation is retained as the default for these methods.
dbbea25
to
ac478f2
Compare
@bors r+ |
📌 Commit ac478f2 has been approved by |
⌛ Testing commit ac478f2 with merge 23e4949bb9f13174569c3f369434398db395ee16... |
💔 Test failed - checks-azure |
This seems to have been part of a Bors batch that was canceled? https://dev.azure.com/rust-lang/rust/_build/results?buildId=24890&view=results Will it be requeued automatically or does it need another r+? |
@bors retry |
…=kennytm Optimize strip_prefix and strip_suffix with str patterns As mentioned in rust-lang#67302 (comment). I'm not sure whether adding these methods to `Pattern` is desirable—but they have default implementations so the change is backwards compatible. Plus it seems like they're slated for wholesale replacement soon anyway? rust-lang#56345 ---- Constructing a Searcher in strip_prefix and strip_suffix is unnecessarily slow when the pattern is a fixed-length string. Add strip_prefix and strip_suffix methods to the Pattern trait, and add optimized implementations of these methods in the str implementation. The old implementation is retained as the default for these methods.
…=kennytm Optimize strip_prefix and strip_suffix with str patterns As mentioned in rust-lang#67302 (comment). I'm not sure whether adding these methods to `Pattern` is desirable—but they have default implementations so the change is backwards compatible. Plus it seems like they're slated for wholesale replacement soon anyway? rust-lang#56345 ---- Constructing a Searcher in strip_prefix and strip_suffix is unnecessarily slow when the pattern is a fixed-length string. Add strip_prefix and strip_suffix methods to the Pattern trait, and add optimized implementations of these methods in the str implementation. The old implementation is retained as the default for these methods.
Rollup of 9 pull requests Successful merges: - rust-lang#69784 (Optimize strip_prefix and strip_suffix with str patterns) - rust-lang#70548 (Add long error code for error E0226) - rust-lang#70555 (resolve, `try_resolve_as_non_binding`: use `delay_span_bug` due to parser recovery) - rust-lang#70561 (remove obsolete comment) - rust-lang#70562 (infer array len from pattern) - rust-lang#70585 (std: Fix over-aligned allocations on wasm32-wasi) - rust-lang#70587 (Add `Rust` to the code snippet) - rust-lang#70588 (Fix incorrect documentation for `str::{split_at, split_at_mut}`) - rust-lang#70613 (more clippy fixes) Failed merges: r? @ghost
Thanks very much, everyone! |
As mentioned in #67302 (comment).
I'm not sure whether adding these methods to
Pattern
is desirable—but they have default implementations so the change is backwards compatible. Plus it seems like they're slated for wholesale replacement soon anyway? #56345Constructing a Searcher in strip_prefix and strip_suffix is
unnecessarily slow when the pattern is a fixed-length string. Add
strip_prefix and strip_suffix methods to the Pattern trait, and add
optimized implementations of these methods in the str implementation.
The old implementation is retained as the default for these methods.