diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 87ad5b0ce30e6..fd54a375f3ea9 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -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)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index ae30cabf5af5b..2e70871fcef87 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -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(&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(&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 [Box]<[str]>. /// /// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`]. diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 49fb21ef5f3ac..f94f92397bb18 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -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)] diff --git a/library/alloctests/tests/string.rs b/library/alloctests/tests/string.rs index d996c55f94660..ecc5b9dc82ed0 100644 --- a/library/alloctests/tests/string.rs +++ b/library/alloctests/tests/string.rs @@ -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::('❌', "✅✅"); + assert_eq!(s, "✅✅"); + + let mut s = String::from(""); + s.replace_last::('🌌', "❌"); + assert_eq!(s, ""); +} + #[test] fn test_extend_ref() { let mut a = "foo".to_string();