Add String::replace_first and String::replace_last

Rebased and modified by zachs18.

Co-authored-by: zachs18 <8355914+zachs18@users.noreply.github.com>
This commit is contained in:
William Venner 2022-06-11 01:01:18 +01:00 committed by Zachary S
parent 695857bc3f
commit 8ed7fca74c
4 changed files with 97 additions and 0 deletions

View file

@ -85,6 +85,7 @@
//
// Library features:
// tidy-alphabetical-start
#![cfg_attr(not(no_global_oom_handling), feature(string_replace_in_place))]
#![feature(alloc_layout_extra)]
#![feature(allocator_api)]
#![feature(array_into_iter_constructors)]

View file

@ -2090,6 +2090,67 @@ impl String {
unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes());
}
/// Replaces the leftmost occurrence of a pattern with another string, in-place.
///
/// This method can be preferred over [`string = string.replacen(..., 1);`][replacen],
/// as it can use the `String`'s existing capacity to prevent a reallocation if
/// sufficient space is available.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(string_replace_in_place)]
///
/// let mut s = String::from("Test Results: ❌❌❌");
///
/// // Replace the leftmost ❌ with a ✅
/// s.replace_first('❌', "✅");
/// assert_eq!(s, "Test Results: ✅❌❌");
/// ```
///
/// [replacen]: ../../std/primitive.str.html#method.replacen
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_replace_in_place", issue = "147949")]
pub fn replace_first<P: Pattern>(&mut self, from: P, to: &str) {
let range = match self.match_indices(from).next() {
Some((start, match_str)) => start..start + match_str.len(),
None => return,
};
self.replace_range(range, to);
}
/// Replaces the rightmost occurrence of a pattern with another string, in-place.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(string_replace_in_place)]
///
/// let mut s = String::from("Test Results: ❌❌❌");
///
/// // Replace the rightmost ❌ with a ✅
/// s.replace_last('❌', "✅");
/// assert_eq!(s, "Test Results: ❌❌✅");
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_replace_in_place", issue = "147949")]
pub fn replace_last<P: Pattern>(&mut self, from: P, to: &str)
where
for<'a> P::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>,
{
let range = match self.rmatch_indices(from).next() {
Some((start, match_str)) => start..start + match_str.len(),
None => return,
};
self.replace_range(range, to);
}
/// Converts this `String` into a <code>[Box]<[str]></code>.
///
/// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`].

View file

@ -36,6 +36,7 @@
#![feature(local_waker)]
#![feature(str_as_str)]
#![feature(strict_provenance_lints)]
#![feature(string_replace_in_place)]
#![feature(vec_deque_pop_if)]
#![feature(vec_deque_truncate_front)]
#![feature(unique_rc_arc)]

View file

@ -719,6 +719,40 @@ fn test_replace_range_evil_end_bound() {
assert_eq!(Ok(""), str::from_utf8(s.as_bytes()));
}
#[test]
fn test_replace_first() {
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
s.replace_first("", "✅✅");
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
s.replace_first("🦀", "😳");
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
let mut s = String::from("");
s.replace_first('❌', "✅✅");
assert_eq!(s, "✅✅");
let mut s = String::from("");
s.replace_first('🌌', "");
assert_eq!(s, "");
}
#[test]
fn test_replace_last() {
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
s.replace_last("", "✅✅");
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
s.replace_last("🦀", "😳");
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
let mut s = String::from("");
s.replace_last::<char>('❌', "✅✅");
assert_eq!(s, "✅✅");
let mut s = String::from("");
s.replace_last::<char>('🌌', "");
assert_eq!(s, "");
}
#[test]
fn test_extend_ref() {
let mut a = "foo".to_string();