Auto merge of #71780 - jcotton42:string_remove_matches, r=joshtriplett

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.
This commit is contained in:
bors 2021-03-19 00:47:37 +00:00
commit eb95acea8a
3 changed files with 84 additions and 0 deletions

View File

@ -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`.

View File

@ -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};

View File

@ -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("α_β_γ");