Skip to content

Commit

Permalink
Auto merge of #71780 - jcotton42:string_remove_matches, r=joshtriplett
Browse files Browse the repository at this point in the history
Implement String::remove_matches

Closes #50206.

I lifted the function help from `@frewsxcv's` original PR (#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.
  • Loading branch information
bors committed Mar 19, 2021
2 parents 1705a7d + a2571cf commit eb95ace
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
56 changes: 56 additions & 0 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,62 @@ impl String {
ch
}

/// Remove all matches of pattern `pat` in the `String`.
///
/// # Examples
///
/// ```
/// #![feature(string_remove_matches)]
/// let mut s = String::from("Trees are not green, the sky is not blue.");
/// s.remove_matches("not ");
/// assert_eq!("Trees are green, the sky is blue.", s);
/// ```
///
/// Matches will be detected and removed iteratively, so in cases where
/// patterns overlap, only the first pattern will be removed:
///
/// ```
/// #![feature(string_remove_matches)]
/// let mut s = String::from("banana");
/// s.remove_matches("ana");
/// assert_eq!("bna", s);
/// ```
#[unstable(feature = "string_remove_matches", reason = "new API", issue = "72826")]
pub fn remove_matches<'a, P>(&'a mut self, pat: P)
where
P: for<'x> Pattern<'x>,
{
use core::str::pattern::Searcher;

let matches = {
let mut searcher = pat.into_searcher(self);
let mut matches = Vec::new();

while let Some(m) = searcher.next_match() {
matches.push(m);
}

matches
};

let len = self.len();
let mut shrunk_by = 0;

// SAFETY: start and end will be on utf8 byte boundaries per
// the Searcher docs
unsafe {
for (start, end) in matches {
ptr::copy(
self.vec.as_mut_ptr().add(end - shrunk_by),
self.vec.as_mut_ptr().add(start - shrunk_by),
len - end,
);
shrunk_by += end - start;
}
self.vec.set_len(len - shrunk_by);
}
}

/// Retains only the characters specified by the predicate.
///
/// In other words, remove all characters `c` such that `f(c)` returns `false`.
Expand Down
1 change: 1 addition & 0 deletions library/alloc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#![feature(slice_partition_dedup)]
#![feature(vec_extend_from_within)]
#![feature(vec_spare_capacity)]
#![feature(string_remove_matches)]

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
Expand Down
27 changes: 27 additions & 0 deletions library/alloc/tests/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,33 @@ fn remove_bad() {
"ศ".to_string().remove(1);
}

#[test]
fn test_remove_matches() {
let mut s = "abc".to_string();

s.remove_matches('b');
assert_eq!(s, "ac");
s.remove_matches('b');
assert_eq!(s, "ac");

let mut s = "abcb".to_string();

s.remove_matches('b');
assert_eq!(s, "ac");

let mut s = "ศไทย中华Việt Nam; foobarศ".to_string();
s.remove_matches('ศ');
assert_eq!(s, "ไทย中华Việt Nam; foobar");

let mut s = "".to_string();
s.remove_matches("");
assert_eq!(s, "");

let mut s = "aaaaa".to_string();
s.remove_matches('a');
assert_eq!(s, "");
}

#[test]
fn test_retain() {
let mut s = String::from("α_β_γ");
Expand Down

0 comments on commit eb95ace

Please sign in to comment.