Skip to content

Commit 32cb8f1

Browse files
committed
Add trim_prefix and trim_suffix for slice and str.
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.
1 parent c6a9554 commit 32cb8f1

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

library/core/src/slice/mod.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2763,6 +2763,89 @@ impl<T> [T] {
27632763
None
27642764
}
27652765

2766+
/// Returns a subslice with the optional prefix removed.
2767+
///
2768+
/// If the slice starts with `prefix`, returns the subslice after the prefix. If `prefix`
2769+
/// is empty or the slice does not start with `prefix`, simply returns the original slice.
2770+
/// If `prefix` is equal to the original slice, returns an empty slice.
2771+
///
2772+
/// # Examples
2773+
///
2774+
/// ```
2775+
/// #![feature(trim_prefix_suffix)]
2776+
///
2777+
/// let v = &[10, 40, 30];
2778+
///
2779+
/// // Prefix present - removes it
2780+
/// assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
2781+
/// assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
2782+
/// assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
2783+
///
2784+
/// // Prefix absent - returns original slice
2785+
/// assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
2786+
/// assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
2787+
///
2788+
/// let prefix : &str = "he";
2789+
/// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
2790+
/// ```
2791+
#[must_use = "returns the subslice without modifying the original"]
2792+
#[unstable(feature = "trim_prefix_suffix", issue = "142312")]
2793+
pub fn trim_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> &[T]
2794+
where
2795+
T: PartialEq,
2796+
{
2797+
// This function will need rewriting if and when SlicePattern becomes more sophisticated.
2798+
let prefix = prefix.as_slice();
2799+
let n = prefix.len();
2800+
if n <= self.len() {
2801+
let (head, tail) = self.split_at(n);
2802+
if head == prefix {
2803+
return tail;
2804+
}
2805+
}
2806+
self
2807+
}
2808+
2809+
/// Returns a subslice with the optional suffix removed.
2810+
///
2811+
/// If the slice ends with `suffix`, returns the subslice before the suffix. If `suffix`
2812+
/// is empty or the slice does not end with `suffix`, simply returns the original slice.
2813+
/// If `suffix` is equal to the original slice, returns an empty slice.
2814+
///
2815+
/// # Examples
2816+
///
2817+
/// ```
2818+
/// #![feature(trim_prefix_suffix)]
2819+
///
2820+
/// let v = &[10, 40, 30];
2821+
///
2822+
/// // Suffix present - removes it
2823+
/// assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
2824+
/// assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
2825+
/// assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
2826+
///
2827+
/// // Suffix absent - returns original slice
2828+
/// assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
2829+
/// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);
2830+
/// ```
2831+
#[must_use = "returns the subslice without modifying the original"]
2832+
#[unstable(feature = "trim_prefix_suffix", issue = "142312")]
2833+
pub fn trim_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> &[T]
2834+
where
2835+
T: PartialEq,
2836+
{
2837+
// This function will need rewriting if and when SlicePattern becomes more sophisticated.
2838+
let suffix = suffix.as_slice();
2839+
let (len, n) = (self.len(), suffix.len());
2840+
if n <= len {
2841+
let (head, tail) = self.split_at(len - n);
2842+
if tail == suffix {
2843+
return head;
2844+
}
2845+
}
2846+
self
2847+
}
2848+
27662849
/// Binary searches this slice for a given element.
27672850
/// If the slice is not sorted, the returned result is unspecified and
27682851
/// meaningless.

library/core/src/str/mod.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,6 +2426,83 @@ impl str {
24262426
suffix.strip_suffix_of(self)
24272427
}
24282428

2429+
/// Returns a string slice with the optional prefix removed.
2430+
///
2431+
/// If the string starts with the pattern `prefix`, returns the substring after the prefix.
2432+
/// Unlike [`strip_prefix`], this method always returns `&str` for easy method chaining,
2433+
/// instead of returning [`Option<&str>`].
2434+
///
2435+
/// If the string does not start with `prefix`, returns the original string unchanged.
2436+
///
2437+
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
2438+
/// function or closure that determines if a character matches.
2439+
///
2440+
/// [`char`]: prim@char
2441+
/// [pattern]: self::pattern
2442+
/// [`strip_prefix`]: Self::strip_prefix
2443+
///
2444+
/// # Examples
2445+
///
2446+
/// ```
2447+
/// #![feature(trim_prefix_suffix)]
2448+
///
2449+
/// // Prefix present - removes it
2450+
/// assert_eq!("foo:bar".trim_prefix("foo:"), "bar");
2451+
/// assert_eq!("foofoo".trim_prefix("foo"), "foo");
2452+
///
2453+
/// // Prefix absent - returns original string
2454+
/// assert_eq!("foo:bar".trim_prefix("bar"), "foo:bar");
2455+
///
2456+
/// // Method chaining example
2457+
/// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
2458+
/// ```
2459+
#[must_use = "this returns the remaining substring as a new slice, \
2460+
without modifying the original"]
2461+
#[unstable(feature = "trim_prefix_suffix", issue = "142312")]
2462+
pub fn trim_prefix<P: Pattern>(&self, prefix: P) -> &str {
2463+
prefix.strip_prefix_of(self).unwrap_or(self)
2464+
}
2465+
2466+
/// Returns a string slice with the optional suffix removed.
2467+
///
2468+
/// If the string ends with the pattern `suffix`, returns the substring before the suffix.
2469+
/// Unlike [`strip_suffix`], this method always returns `&str` for easy method chaining,
2470+
/// instead of returning [`Option<&str>`].
2471+
///
2472+
/// If the string does not end with `suffix`, returns the original string unchanged.
2473+
///
2474+
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
2475+
/// function or closure that determines if a character matches.
2476+
///
2477+
/// [`char`]: prim@char
2478+
/// [pattern]: self::pattern
2479+
/// [`strip_suffix`]: Self::strip_suffix
2480+
///
2481+
/// # Examples
2482+
///
2483+
/// ```
2484+
/// #![feature(trim_prefix_suffix)]
2485+
///
2486+
/// // Suffix present - removes it
2487+
/// assert_eq!("bar:foo".trim_suffix(":foo"), "bar");
2488+
/// assert_eq!("foofoo".trim_suffix("foo"), "foo");
2489+
///
2490+
/// // Suffix absent - returns original string
2491+
/// assert_eq!("bar:foo".trim_suffix("bar"), "bar:foo");
2492+
///
2493+
/// // Method chaining example
2494+
/// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
2495+
/// ```
2496+
#[must_use = "this returns the remaining substring as a new slice, \
2497+
without modifying the original"]
2498+
#[unstable(feature = "trim_prefix_suffix", issue = "142312")]
2499+
pub fn trim_suffix<P: Pattern>(&self, suffix: P) -> &str
2500+
where
2501+
for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
2502+
{
2503+
suffix.strip_suffix_of(self).unwrap_or(self)
2504+
}
2505+
24292506
/// Returns a string slice with all suffixes that match a pattern
24302507
/// repeatedly removed.
24312508
///

0 commit comments

Comments
 (0)