From a2902ed5f04410eb99511c1bc1430c4dbdb83b5a Mon Sep 17 00:00:00 2001 From: William Venner Date: Sat, 11 Jun 2022 01:01:18 +0100 Subject: [PATCH 1/4] Add `String::replace_first` and `String::replace_last` Rebased by zachs18. Co-authored-by: zachs18 <8355914+zachs18@users.noreply.github.com> --- library/alloc/src/lib.rs | 1 + library/alloc/src/string.rs | 57 +++++++++++++++++++++++++++++++++++ library/alloc/tests/lib.rs | 1 + library/alloc/tests/string.rs | 34 +++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 40759cb0ba83c..c9f4a4a58df97 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -134,6 +134,7 @@ #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] +#![feature(string_replace_in_place)] #![feature(set_ptr_value)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index c5378d78d591b..9bbb629ae6344 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2003,6 +2003,63 @@ 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 should be preferred over [`String::replacen(..., 1)`](str::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: ✅❌❌"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_replace_in_place", issue = "none")] + 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 = "none")] + 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/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 393bdfe48b741..5af282d107b0c 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -37,6 +37,7 @@ #![feature(local_waker)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] +#![feature(string_replace_in_place)] #![feature(vec_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 1c8bff1564db2..598da62aeca00 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -710,6 +710,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(); From 33aad26dc6b576878bffe5568e2bc6930b2820b1 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sat, 27 Apr 2024 01:31:58 -0500 Subject: [PATCH 2/4] tidy --- library/alloc/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index c9f4a4a58df97..1f725ff783ac7 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -134,7 +134,6 @@ #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] -#![feature(string_replace_in_place)] #![feature(set_ptr_value)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] @@ -144,6 +143,7 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] +#![feature(string_replace_in_place)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] From 29b5877bab2425a7442e7ca8aaa1489740c5ab7d Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sat, 27 Apr 2024 02:13:33 -0500 Subject: [PATCH 3/4] Hard-code doclink to avoid issue 98941 Also line-wrap doc-comment. --- library/alloc/src/string.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 9bbb629ae6344..28f9b381bdeda 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2005,7 +2005,9 @@ impl String { /// Replaces the leftmost occurrence of a pattern with another string, in-place. /// - /// This method should be preferred over [`String::replacen(..., 1)`](str::replacen) as it can use the `String`'s existing capacity to prevent a reallocation if sufficient space is available. + /// This method should be preferred over [`String::replacen(..., 1)`][replacen] + /// as it can use the `String`'s existing capacity to prevent a reallocation if + /// sufficient space is available. /// /// # Examples /// @@ -2020,6 +2022,8 @@ impl String { /// 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 = "none")] pub fn replace_first(&mut self, from: P, to: &str) { From 0243f5c0c6a234b290eb15e32e986333e4cc770f Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sat, 14 Dec 2024 20:55:03 -0600 Subject: [PATCH 4/4] Fix build under --cfg no_global_oom_handling --- library/alloc/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 1f725ff783ac7..a4933c2011579 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -92,6 +92,7 @@ // Library features: // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(async_closure))] +#![cfg_attr(not(no_global_oom_handling), feature(string_replace_in_place))] #![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -143,7 +144,6 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] -#![feature(string_replace_in_place)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)]