Skip to content

Commit 6dc294b

Browse files
committed
Add String::replace_first and String::replace_last
1 parent b0af276 commit 6dc294b

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
#![feature(ptr_metadata)]
140140
#![feature(ptr_sub_ptr)]
141141
#![feature(receiver_trait)]
142+
#![feature(string_replace_in_place)]
142143
#![feature(set_ptr_value)]
143144
#![feature(sized_type_properties)]
144145
#![feature(slice_from_ptr_range)]

library/alloc/src/string.rs

+57
Original file line numberDiff line numberDiff line change
@@ -1985,6 +1985,63 @@ impl String {
19851985
unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes());
19861986
}
19871987

1988+
/// Replaces the leftmost occurrence of a pattern with another string, in-place.
1989+
///
1990+
/// 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.
1991+
///
1992+
/// # Examples
1993+
///
1994+
/// Basic usage:
1995+
///
1996+
/// ```
1997+
/// #![feature(string_replace_in_place)]
1998+
///
1999+
/// let mut s = String::from("Test Results: ❌❌❌");
2000+
///
2001+
/// // Replace the leftmost ❌ with a ✅
2002+
/// s.replace_first('❌', "✅");
2003+
/// assert_eq!(s, "Test Results: ✅❌❌");
2004+
/// ```
2005+
#[cfg(not(no_global_oom_handling))]
2006+
#[unstable(feature = "string_replace_in_place", issue = "none")]
2007+
pub fn replace_first<P: for<'a> Pattern<'a>>(&mut self, from: P, to: &str) {
2008+
let range = match self.match_indices(from).next() {
2009+
Some((start, match_str)) => start..start + match_str.len(),
2010+
None => return,
2011+
};
2012+
2013+
self.replace_range(range, to);
2014+
}
2015+
2016+
/// Replaces the rightmost occurrence of a pattern with another string, in-place.
2017+
///
2018+
/// # Examples
2019+
///
2020+
/// Basic usage:
2021+
///
2022+
/// ```
2023+
/// #![feature(string_replace_in_place)]
2024+
///
2025+
/// let mut s = String::from("Test Results: ❌❌❌");
2026+
///
2027+
/// // Replace the rightmost ❌ with a ✅
2028+
/// s.replace_last::<char>('❌', "✅");
2029+
/// assert_eq!(s, "Test Results: ❌❌✅");
2030+
/// ```
2031+
#[cfg(not(no_global_oom_handling))]
2032+
#[unstable(feature = "string_replace_in_place", issue = "none")]
2033+
pub fn replace_last<P>(&mut self, from: P, to: &str)
2034+
where
2035+
P: for<'a> Pattern<'a, Searcher: str::pattern::ReverseSearcher<'a>>,
2036+
{
2037+
let range = match self.rmatch_indices(from).next() {
2038+
Some((start, match_str)) => start..start + match_str.len(),
2039+
None => return,
2040+
};
2041+
2042+
self.replace_range(range, to);
2043+
}
2044+
19882045
/// Converts this `String` into a <code>[Box]<[str]></code>.
19892046
///
19902047
/// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`].

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#![feature(iter_next_chunk)]
2929
#![feature(round_char_boundary)]
3030
#![feature(slice_partition_dedup)]
31+
#![feature(string_replace_in_place)]
3132
#![feature(string_remove_matches)]
3233
#![feature(const_btree_len)]
3334
#![feature(const_trait_impl)]

library/alloc/tests/string.rs

+34
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,40 @@ fn test_replace_range_evil_end_bound() {
682682
assert_eq!(Ok(""), str::from_utf8(s.as_bytes()));
683683
}
684684

685+
#[test]
686+
fn test_replace_first() {
687+
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
688+
s.replace_first("❌", "✅✅");
689+
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
690+
s.replace_first("🦀", "😳");
691+
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
692+
693+
let mut s = String::from("❌");
694+
s.replace_first('❌', "✅✅");
695+
assert_eq!(s, "✅✅");
696+
697+
let mut s = String::from("");
698+
s.replace_first('🌌', "❌");
699+
assert_eq!(s, "");
700+
}
701+
702+
#[test]
703+
fn test_replace_last() {
704+
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
705+
s.replace_last::<&str>("❌", "✅✅");
706+
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
707+
s.replace_last::<&str>("🦀", "😳");
708+
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
709+
710+
let mut s = String::from("❌");
711+
s.replace_last::<char>('❌', "✅✅");
712+
assert_eq!(s, "✅✅");
713+
714+
let mut s = String::from("");
715+
s.replace_last::<char>('🌌', "❌");
716+
assert_eq!(s, "");
717+
}
718+
685719
#[test]
686720
fn test_extend_ref() {
687721
let mut a = "foo".to_string();

0 commit comments

Comments
 (0)