diff options
| author | Trevor Gross <t.gross35@gmail.com> | 2025-06-20 13:35:58 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-20 13:35:58 -0400 |
| commit | 851fbcb0928e55296212bb060cd82cf3350087c2 (patch) | |
| tree | 608f300fac2b481b69d8326155fd849260a00331 | |
| parent | 9c4ff566babe632af5e30281a822d1ae9972873b (diff) | |
| parent | 32cb8f1537a3c0948e01cb90c850d9f60e3139ed (diff) | |
| download | rust-851fbcb0928e55296212bb060cd82cf3350087c2.tar.gz rust-851fbcb0928e55296212bb060cd82cf3350087c2.zip | |
Rollup merge of #142331 - deven:trim_prefix_suffix, r=Amanieu
Add `trim_prefix` and `trim_suffix` methods for both `slice` and `str` types.
Implements `trim_prefix` and `trim_suffix` methods for both `slice` and `str` types, which remove at most one occurrence of a prefix/suffix while always returning a string/slice (rather than Option), enabling easy method chaining.
## Tracking issue
rust-lang/rust#142312
## API
```rust
impl str {
pub fn trim_prefix<P: Pattern>(&self, prefix: P) -> &str;
pub fn trim_suffix<P: Pattern>(&self, suffix: P) -> &str
where
for<'a> P::Searcher<'a>: ReverseSearcher<'a>;
}
impl<T> [T] {
pub fn trim_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> &[T]
where
T: PartialEq;
pub fn trim_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> &[T]
where
T: PartialEq;
}
```
## Examples
```rust
// Method chaining
assert_eq!(" <https://example.com/> ".trim().trim_prefix('<').trim_suffix('>').trim(), "https://example.com/");
// Slices
let v = &[10, 40, 30];
assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
```
## ACP
Originally proposed in rust-lang/libs-team#597
| -rw-r--r-- | library/core/src/slice/mod.rs | 83 | ||||
| -rw-r--r-- | library/core/src/str/mod.rs | 77 |
2 files changed, 160 insertions, 0 deletions
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index c26bbad087a..3a3f44c6b85 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2764,6 +2764,89 @@ impl<T> [T] { None } + /// Returns a subslice with the optional prefix removed. + /// + /// If the slice starts with `prefix`, returns the subslice after the prefix. If `prefix` + /// is empty or the slice does not start with `prefix`, simply returns the original slice. + /// If `prefix` is equal to the original slice, returns an empty slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(trim_prefix_suffix)] + /// + /// let v = &[10, 40, 30]; + /// + /// // Prefix present - removes it + /// assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]); + /// assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]); + /// assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]); + /// + /// // Prefix absent - returns original slice + /// assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]); + /// assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]); + /// + /// let prefix : &str = "he"; + /// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref()); + /// ``` + #[must_use = "returns the subslice without modifying the original"] + #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + pub fn trim_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> &[T] + where + T: PartialEq, + { + // This function will need rewriting if and when SlicePattern becomes more sophisticated. + let prefix = prefix.as_slice(); + let n = prefix.len(); + if n <= self.len() { + let (head, tail) = self.split_at(n); + if head == prefix { + return tail; + } + } + self + } + + /// Returns a subslice with the optional suffix removed. + /// + /// If the slice ends with `suffix`, returns the subslice before the suffix. If `suffix` + /// is empty or the slice does not end with `suffix`, simply returns the original slice. + /// If `suffix` is equal to the original slice, returns an empty slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(trim_prefix_suffix)] + /// + /// let v = &[10, 40, 30]; + /// + /// // Suffix present - removes it + /// assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]); + /// assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]); + /// assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]); + /// + /// // Suffix absent - returns original slice + /// assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]); + /// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]); + /// ``` + #[must_use = "returns the subslice without modifying the original"] + #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + pub fn trim_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> &[T] + where + T: PartialEq, + { + // This function will need rewriting if and when SlicePattern becomes more sophisticated. + let suffix = suffix.as_slice(); + let (len, n) = (self.len(), suffix.len()); + if n <= len { + let (head, tail) = self.split_at(len - n); + if tail == suffix { + return head; + } + } + self + } + /// Binary searches this slice for a given element. /// If the slice is not sorted, the returned result is unspecified and /// meaningless. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 41834793d22..5051b2288fd 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2426,6 +2426,83 @@ impl str { suffix.strip_suffix_of(self) } + /// Returns a string slice with the optional prefix removed. + /// + /// If the string starts with the pattern `prefix`, returns the substring after the prefix. + /// Unlike [`strip_prefix`], this method always returns `&str` for easy method chaining, + /// instead of returning [`Option<&str>`]. + /// + /// If the string does not start with `prefix`, returns the original string unchanged. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// [`strip_prefix`]: Self::strip_prefix + /// + /// # Examples + /// + /// ``` + /// #![feature(trim_prefix_suffix)] + /// + /// // Prefix present - removes it + /// assert_eq!("foo:bar".trim_prefix("foo:"), "bar"); + /// assert_eq!("foofoo".trim_prefix("foo"), "foo"); + /// + /// // Prefix absent - returns original string + /// assert_eq!("foo:bar".trim_prefix("bar"), "foo:bar"); + /// + /// // Method chaining example + /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/"); + /// ``` + #[must_use = "this returns the remaining substring as a new slice, \ + without modifying the original"] + #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + pub fn trim_prefix<P: Pattern>(&self, prefix: P) -> &str { + prefix.strip_prefix_of(self).unwrap_or(self) + } + + /// Returns a string slice with the optional suffix removed. + /// + /// If the string ends with the pattern `suffix`, returns the substring before the suffix. + /// Unlike [`strip_suffix`], this method always returns `&str` for easy method chaining, + /// instead of returning [`Option<&str>`]. + /// + /// If the string does not end with `suffix`, returns the original string unchanged. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// [`strip_suffix`]: Self::strip_suffix + /// + /// # Examples + /// + /// ``` + /// #![feature(trim_prefix_suffix)] + /// + /// // Suffix present - removes it + /// assert_eq!("bar:foo".trim_suffix(":foo"), "bar"); + /// assert_eq!("foofoo".trim_suffix("foo"), "foo"); + /// + /// // Suffix absent - returns original string + /// assert_eq!("bar:foo".trim_suffix("bar"), "bar:foo"); + /// + /// // Method chaining example + /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/"); + /// ``` + #[must_use = "this returns the remaining substring as a new slice, \ + without modifying the original"] + #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + pub fn trim_suffix<P: Pattern>(&self, suffix: P) -> &str + where + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + { + suffix.strip_suffix_of(self).unwrap_or(self) + } + /// Returns a string slice with all suffixes that match a pattern /// repeatedly removed. /// |
