diff --git a/src/doc/unstable-book/src/library-features/string-retain.md b/src/doc/unstable-book/src/library-features/string-retain.md new file mode 100644 index 0000000000000..049444aa49bdd --- /dev/null +++ b/src/doc/unstable-book/src/library-features/string-retain.md @@ -0,0 +1,23 @@ +# `string_retain` + +The tracking issue for this feature is: [#43874] + +[#43874]: https://github.com/rust-lang/rust/issues/43874 + +------------------------ + +Retains only the characters specified by the predicate. + +In other words, remove all characters `c` such that `f(c)` returns `false`. +This method operates in place and preserves the order of the retained +characters. + +```rust +#![feature(string_retain)] + +let mut s = String::from("f_o_ob_ar"); + +s.retain(|c| c != '_'); + +assert_eq!(s, "foobar"); +``` diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 622cc68964bf7..4acf42edb412f 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1031,6 +1031,57 @@ impl String { ch } + /// Retains only the characters specified by the predicate. + /// + /// In other words, remove all characters `c` such that `f(c)` returns `false`. + /// This method operates in place and preserves the order of the retained + /// characters. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_retain)] + /// + /// let mut s = String::from("f_o_ob_ar"); + /// + /// s.retain(|c| c != '_'); + /// + /// assert_eq!(s, "foobar"); + /// ``` + #[inline] + #[unstable(feature = "string_retain", issue = "43874")] + pub fn retain(&mut self, mut f: F) + where F: FnMut(char) -> bool + { + let len = self.len(); + let mut del_bytes = 0; + let mut idx = 0; + + while idx < len { + let ch = unsafe { + self.slice_unchecked(idx, len).chars().next().unwrap() + }; + let ch_len = ch.len_utf8(); + + if !f(ch) { + del_bytes += ch_len; + } else if del_bytes > 0 { + unsafe { + ptr::copy(self.vec.as_ptr().offset(idx as isize), + self.vec.as_mut_ptr().offset((idx - del_bytes) as isize), + ch_len); + } + } + + // Point idx to the next char + idx += ch_len; + } + + if del_bytes > 0 { + unsafe { self.vec.set_len(len - del_bytes); } + } + } + /// Inserts a character into this `String` at a byte position. /// /// This is an `O(n)` operation as it requires copying every element in the diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index c6d70ee7575f8..d4f3a89ee99e2 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -26,6 +26,7 @@ #![feature(splice)] #![feature(str_checked_slicing)] #![feature(str_escape)] +#![feature(string_retain)] #![feature(test)] #![feature(unboxed_closures)] #![feature(unicode)] diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index b1731b2a5dcaa..f5c124c6b4458 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -332,6 +332,26 @@ fn remove_bad() { "ศ".to_string().remove(1); } +#[test] +fn test_retain() { + let mut s = String::from("α_β_γ"); + + s.retain(|_| true); + assert_eq!(s, "α_β_γ"); + + s.retain(|c| c != '_'); + assert_eq!(s, "αβγ"); + + s.retain(|c| c != 'β'); + assert_eq!(s, "αγ"); + + s.retain(|c| c == 'α'); + assert_eq!(s, "α"); + + s.retain(|_| false); + assert_eq!(s, ""); +} + #[test] fn insert() { let mut s = "foobar".to_string();