From 9badc33cda41acadc395f1597947b185f5a9b2a3 Mon Sep 17 00:00:00 2001 From: SOFe Date: Mon, 25 Nov 2019 19:36:47 +0800 Subject: [PATCH 1/4] Add str::strip_prefix and str::strip_suffix --- src/libcore/str/mod.rs | 69 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 25b7eec5b3343..2527aee58cd4d 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -8,7 +8,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use self::pattern::Pattern; -use self::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; +use self::pattern::{Searcher, SearchStep, ReverseSearcher, DoubleEndedSearcher}; use crate::char; use crate::fmt::{self, Write}; @@ -3791,6 +3791,73 @@ impl str { } } + /// Returns a string slice with the prefix removed. + /// + /// If the string starts with the pattern `prefix`, `Some` is returned with the substring where + /// the prefix is removed. Unlike `trim_start_matches`, this method removes the prefix exactly + /// once. + /// + /// If the string does not start with `prefix`, it is removed. + /// + /// # Examples + /// + /// ``` + /// #![feature(str_strip)] + /// + /// assert_eq!("foobar".strip_prefix("foo"), Some("bar")); + /// assert_eq!("foobar".strip_prefix("bar"), None); + /// assert_eq!("foofoo".strip_prefix("foo"), Some("foo")); + /// ``` + #[must_use = "this returns the remaining substring as a new slice, \ + without modifying the original"] + #[unstable(feature = "str_strip", reason = "newly added", issue = "0")] + pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { + let mut matcher = prefix.into_searcher(self); + if let SearchStep::Match(start, len) = matcher.next() { + debug_assert_eq!(start, 0); + unsafe { + Some(self.get_unchecked(len..)) + } + } else { + None + } + } + + /// Returns a string slice with the suffix removed. + /// + /// If the string starts with the pattern `suffix`, `Some` is returned with the substring where + /// the suffix is removed. Unlike `trim_end_matches`, this method removes the suffix exactly + /// once. + /// + /// If the string does not start with `suffix`, it is removed. + /// + /// # Examples + /// + /// ``` + /// #![feature(str_strip)] + /// assert_eq!("barfoo".strip_suffix("foo"), Some("bar")); + /// assert_eq!("barfoo".strip_suffix("bar"), None); + /// assert_eq!("foofoo".strip_suffix("foo"), Some("foo")); + /// ``` + #[must_use = "this returns the remaining substring as a new slice, \ + without modifying the original"] + #[unstable(feature = "str_strip", reason = "newly added", issue = "0")] + pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a str> + where + P: Pattern<'a>, +

>::Searcher: ReverseSearcher<'a>, + { + let mut matcher = suffix.into_searcher(self); + if let SearchStep::Match(start, end) = matcher.next_back() { + debug_assert_eq!(end, self.len()); + unsafe { + Some(self.get_unchecked(..start)) + } + } else { + None + } + } + /// Returns a string slice with all suffixes that match a pattern /// repeatedly removed. /// From 2e2e0dfc1acfb69be9575d320418d04f3caf64a7 Mon Sep 17 00:00:00 2001 From: SOFe Date: Tue, 26 Nov 2019 17:42:43 +0800 Subject: [PATCH 2/4] Improved comments to clarify sasumptions in str::strip_prefix --- src/libcore/str/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 2527aee58cd4d..4367cc54e70db 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3797,7 +3797,7 @@ impl str { /// the prefix is removed. Unlike `trim_start_matches`, this method removes the prefix exactly /// once. /// - /// If the string does not start with `prefix`, it is removed. + /// If the string does not start with `prefix`, `None` is returned. /// /// # Examples /// @@ -3814,8 +3814,9 @@ impl str { pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { let mut matcher = prefix.into_searcher(self); if let SearchStep::Match(start, len) = matcher.next() { - debug_assert_eq!(start, 0); + debug_assert_eq!(start, 0, "The first search step from Searcher must start from the front"); unsafe { + // Searcher is known to return valid indices. Some(self.get_unchecked(len..)) } } else { @@ -3825,11 +3826,11 @@ impl str { /// Returns a string slice with the suffix removed. /// - /// If the string starts with the pattern `suffix`, `Some` is returned with the substring where + /// If the string ends with the pattern `suffix`, `Some` is returned with the substring where /// the suffix is removed. Unlike `trim_end_matches`, this method removes the suffix exactly /// once. /// - /// If the string does not start with `suffix`, it is removed. + /// If the string does not end with `suffix`, `None` is returned. /// /// # Examples /// @@ -3849,8 +3850,9 @@ impl str { { let mut matcher = suffix.into_searcher(self); if let SearchStep::Match(start, end) = matcher.next_back() { - debug_assert_eq!(end, self.len()); + debug_assert_eq!(end, self.len(), "The first search step from ReverseSearcher must include the last character"); unsafe { + // Searcher is known to return valid indices. Some(self.get_unchecked(..start)) } } else { From 4718e20fcf32aa36f23e18ecf3c9ca479a6cce4c Mon Sep 17 00:00:00 2001 From: SOFe Date: Tue, 26 Nov 2019 19:33:06 +0800 Subject: [PATCH 3/4] Fixed formatting issues --- src/libcore/str/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 4367cc54e70db..f0c2fc9789487 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3814,7 +3814,8 @@ impl str { pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { let mut matcher = prefix.into_searcher(self); if let SearchStep::Match(start, len) = matcher.next() { - debug_assert_eq!(start, 0, "The first search step from Searcher must start from the front"); + debug_assert_eq!(start, 0, "The first search step from Searcher \ + must include the first character"); unsafe { // Searcher is known to return valid indices. Some(self.get_unchecked(len..)) @@ -3850,7 +3851,8 @@ impl str { { let mut matcher = suffix.into_searcher(self); if let SearchStep::Match(start, end) = matcher.next_back() { - debug_assert_eq!(end, self.len(), "The first search step from ReverseSearcher must include the last character"); + debug_assert_eq!(end, self.len(), "The first search step from ReverseSearcher \ + must include the last character"); unsafe { // Searcher is known to return valid indices. Some(self.get_unchecked(..start)) From 6176051dd0605b77c304aa647f277f4b069763a9 Mon Sep 17 00:00:00 2001 From: SOFe Date: Sun, 15 Dec 2019 17:07:57 +0800 Subject: [PATCH 4/4] Set tracking issue for str_strip --- src/libcore/str/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f0c2fc9789487..074825ec27ff9 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3810,7 +3810,7 @@ impl str { /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[unstable(feature = "str_strip", reason = "newly added", issue = "0")] + #[unstable(feature = "str_strip", reason = "newly added", issue = "67302")] pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { let mut matcher = prefix.into_searcher(self); if let SearchStep::Match(start, len) = matcher.next() { @@ -3843,7 +3843,7 @@ impl str { /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[unstable(feature = "str_strip", reason = "newly added", issue = "0")] + #[unstable(feature = "str_strip", reason = "newly added", issue = "67302")] pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a str> where P: Pattern<'a>,