about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/alloc/src/string.rs56
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/alloc/tests/string.rs27
3 files changed, 84 insertions, 0 deletions
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index b567d0a2fe2..45adebf77cb 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -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`.
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index 799499b9b77..feeb17e87da 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -21,6 +21,7 @@
 #![feature(slice_group_by)]
 #![feature(vec_extend_from_within)]
 #![feature(vec_spare_capacity)]
+#![feature(string_remove_matches)]
 
 use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs
index f3d74e0514d..9ec0ee97ab9 100644
--- a/library/alloc/tests/string.rs
+++ b/library/alloc/tests/string.rs
@@ -366,6 +366,33 @@ fn remove_bad() {
 }
 
 #[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("α_β_γ");