Skip to content

Commit 2523b66

Browse files
authored
Rollup merge of rust-lang#71780 - jcotton42:string_remove_matches, r=joshtriplett
Implement String::remove_matches Closes rust-lang#50206. I lifted the function help from `@frewsxcv's` original PR (rust-lang#50015), hope they don't mind. I'm also wondering whether it would be useful for `remove_matches` to collect up the removed substrings into a `Vec` and return them, right now they're just overwritten by the copy and lost.
2 parents 1705a7d + a2571cf commit 2523b66

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

library/alloc/src/string.rs

+56
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,62 @@ impl String {
12021202
ch
12031203
}
12041204

1205+
/// Remove all matches of pattern `pat` in the `String`.
1206+
///
1207+
/// # Examples
1208+
///
1209+
/// ```
1210+
/// #![feature(string_remove_matches)]
1211+
/// let mut s = String::from("Trees are not green, the sky is not blue.");
1212+
/// s.remove_matches("not ");
1213+
/// assert_eq!("Trees are green, the sky is blue.", s);
1214+
/// ```
1215+
///
1216+
/// Matches will be detected and removed iteratively, so in cases where
1217+
/// patterns overlap, only the first pattern will be removed:
1218+
///
1219+
/// ```
1220+
/// #![feature(string_remove_matches)]
1221+
/// let mut s = String::from("banana");
1222+
/// s.remove_matches("ana");
1223+
/// assert_eq!("bna", s);
1224+
/// ```
1225+
#[unstable(feature = "string_remove_matches", reason = "new API", issue = "72826")]
1226+
pub fn remove_matches<'a, P>(&'a mut self, pat: P)
1227+
where
1228+
P: for<'x> Pattern<'x>,
1229+
{
1230+
use core::str::pattern::Searcher;
1231+
1232+
let matches = {
1233+
let mut searcher = pat.into_searcher(self);
1234+
let mut matches = Vec::new();
1235+
1236+
while let Some(m) = searcher.next_match() {
1237+
matches.push(m);
1238+
}
1239+
1240+
matches
1241+
};
1242+
1243+
let len = self.len();
1244+
let mut shrunk_by = 0;
1245+
1246+
// SAFETY: start and end will be on utf8 byte boundaries per
1247+
// the Searcher docs
1248+
unsafe {
1249+
for (start, end) in matches {
1250+
ptr::copy(
1251+
self.vec.as_mut_ptr().add(end - shrunk_by),
1252+
self.vec.as_mut_ptr().add(start - shrunk_by),
1253+
len - end,
1254+
);
1255+
shrunk_by += end - start;
1256+
}
1257+
self.vec.set_len(len - shrunk_by);
1258+
}
1259+
}
1260+
12051261
/// Retains only the characters specified by the predicate.
12061262
///
12071263
/// In other words, remove all characters `c` such that `f(c)` returns `false`.

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#![feature(slice_partition_dedup)]
2323
#![feature(vec_extend_from_within)]
2424
#![feature(vec_spare_capacity)]
25+
#![feature(string_remove_matches)]
2526

2627
use std::collections::hash_map::DefaultHasher;
2728
use std::hash::{Hash, Hasher};

library/alloc/tests/string.rs

+27
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,33 @@ fn remove_bad() {
365365
"ศ".to_string().remove(1);
366366
}
367367

368+
#[test]
369+
fn test_remove_matches() {
370+
let mut s = "abc".to_string();
371+
372+
s.remove_matches('b');
373+
assert_eq!(s, "ac");
374+
s.remove_matches('b');
375+
assert_eq!(s, "ac");
376+
377+
let mut s = "abcb".to_string();
378+
379+
s.remove_matches('b');
380+
assert_eq!(s, "ac");
381+
382+
let mut s = "ศไทย中华Việt Nam; foobarศ".to_string();
383+
s.remove_matches('ศ');
384+
assert_eq!(s, "ไทย中华Việt Nam; foobar");
385+
386+
let mut s = "".to_string();
387+
s.remove_matches("");
388+
assert_eq!(s, "");
389+
390+
let mut s = "aaaaa".to_string();
391+
s.remove_matches('a');
392+
assert_eq!(s, "");
393+
}
394+
368395
#[test]
369396
fn test_retain() {
370397
let mut s = String::from("α_β_γ");

0 commit comments

Comments
 (0)