Skip to content

Commit 242edde

Browse files
committed
Add String::replace_first and String::replace_last
1 parent 051d117 commit 242edde

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
#![feature(pattern)]
122122
#![feature(ptr_internals)]
123123
#![feature(receiver_trait)]
124+
#![feature(string_replace_in_place)]
124125
#![feature(set_ptr_value)]
125126
#![feature(slice_group_by)]
126127
#![feature(slice_ptr_get)]

library/alloc/src/string.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,63 @@ impl String {
17401740
unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes());
17411741
}
17421742

1743+
/// Replaces the leftmost occurrence of a pattern with another string, in-place.
1744+
///
1745+
/// 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.
1746+
///
1747+
/// # Examples
1748+
///
1749+
/// Basic usage:
1750+
///
1751+
/// ```
1752+
/// #![feature(string_replace_in_place)]
1753+
///
1754+
/// let mut s = String::from("Test Results: ❌❌❌");
1755+
///
1756+
/// // Replace the leftmost ❌ with a ✅
1757+
/// s.replace_first('❌', "✅");
1758+
/// assert_eq!(s, "Test Results: ✅❌❌");
1759+
/// ```
1760+
#[cfg(not(no_global_oom_handling))]
1761+
#[unstable(feature = "string_replace_in_place", issue = "none")]
1762+
pub fn replace_first<P: for<'a> Pattern<'a>>(&mut self, from: P, to: &str) {
1763+
let range = match self.match_indices(from).next() {
1764+
Some((start, match_str)) => start..start + match_str.len(),
1765+
None => return,
1766+
};
1767+
1768+
self.replace_range(range, to);
1769+
}
1770+
1771+
/// Replaces the rightmost occurrence of a pattern with another string, in-place.
1772+
///
1773+
/// # Examples
1774+
///
1775+
/// Basic usage:
1776+
///
1777+
/// ```
1778+
/// #![feature(string_replace_in_place)]
1779+
///
1780+
/// let mut s = String::from("Test Results: ❌❌❌");
1781+
///
1782+
/// // Replace the rightmost ❌ with a ✅
1783+
/// s.replace_last::<char>('❌', "✅");
1784+
/// assert_eq!(s, "Test Results: ❌❌✅");
1785+
/// ```
1786+
#[cfg(not(no_global_oom_handling))]
1787+
#[unstable(feature = "string_replace_in_place", issue = "none")]
1788+
pub fn replace_last<P>(&mut self, from: P, to: &str)
1789+
where
1790+
P: for<'a> Pattern<'a, Searcher: str::pattern::ReverseSearcher<'a>>,
1791+
{
1792+
let range = match self.rmatch_indices(from).next() {
1793+
Some((start, match_str)) => start..start + match_str.len(),
1794+
None => return,
1795+
};
1796+
1797+
self.replace_range(range, to);
1798+
}
1799+
17431800
/// Converts this `String` into a <code>[Box]<[str]></code>.
17441801
///
17451802
/// This will drop any excess capacity.

library/alloc/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#![feature(inplace_iteration)]
3131
#![feature(iter_advance_by)]
3232
#![feature(round_char_boundary)]
33+
#![feature(string_replace_in_place)]
3334
#![feature(slice_group_by)]
3435
#![feature(slice_partition_dedup)]
3536
#![feature(string_remove_matches)]

library/alloc/tests/string.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,40 @@ fn test_replace_range_evil_end_bound() {
639639
assert_eq!(Ok(""), str::from_utf8(s.as_bytes()));
640640
}
641641

642+
#[test]
643+
fn test_replace_first() {
644+
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
645+
s.replace_first("❌", "✅✅");
646+
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
647+
s.replace_first("🦀", "😳");
648+
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
649+
650+
let mut s = String::from("❌");
651+
s.replace_first('❌', "✅✅");
652+
assert_eq!(s, "✅✅");
653+
654+
let mut s = String::from("");
655+
s.replace_first('🌌', "❌");
656+
assert_eq!(s, "");
657+
}
658+
659+
#[test]
660+
fn test_replace_last() {
661+
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
662+
s.replace_last::<&str>("❌", "✅✅");
663+
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
664+
s.replace_last::<&str>("🦀", "😳");
665+
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
666+
667+
let mut s = String::from("❌");
668+
s.replace_last::<char>('❌', "✅✅");
669+
assert_eq!(s, "✅✅");
670+
671+
let mut s = String::from("");
672+
s.replace_last::<char>('🌌', "❌");
673+
assert_eq!(s, "");
674+
}
675+
642676
#[test]
643677
fn test_extend_ref() {
644678
let mut a = "foo".to_string();

0 commit comments

Comments
 (0)