Skip to content

Commit

Permalink
Auto merge of #43500 - murarth:string-retain, r=alexcrichton
Browse files Browse the repository at this point in the history
Add method `String::retain`

Behaves like `Vec::retain`, accepting a predicate `FnMut(char) -> bool`
and reducing the string to only characters for which the predicate
returns `true`.
  • Loading branch information
bors committed Aug 15, 2017
2 parents 1b08e0f + 618ac89 commit 82be83c
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/doc/unstable-book/src/library-features/string-retain.md
Original file line number Diff line number Diff line change
@@ -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");
```
51 changes: 51 additions & 0 deletions src/liballoc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,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<F>(&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
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#![feature(slice_rotate)]
#![feature(splice)]
#![feature(str_escape)]
#![feature(string_retain)]
#![feature(test)]
#![feature(unboxed_closures)]
#![feature(unicode)]
Expand Down
20 changes: 20 additions & 0 deletions src/liballoc/tests/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 82be83c

Please sign in to comment.