Strings and Text Processing
remove() and retain()
String provides several in-place removal methods—.pop(), .remove(idx), .truncate(new_len), .clear(), and .retain(predicate)—that delete characters without reallocating the underlying heap buffer.
Summary of In-Place Removal Methods on String
| Method | Signature | Time Complexity | Behavior |
|---|---|---|---|
s.pop() |
fn pop(&mut self) -> Option<char> |
$O(1)$ | Removes and returns the last char, or None if empty |
s.remove(idx) |
fn remove(&mut self, idx: usize) -> char |
$O(n)$ | Removes and returns the char at byte offset idx, shifting trailing bytes left (panics if not on a char boundary) |
s.retain(f) |
fn retain<F: FnMut(char) -> bool>(&mut self, f: F) |
$O(n)$ | Keeps only chars where f(ch) returns true in a single linear pass |
s.truncate(len) |
fn truncate(&mut self, new_len: usize) |
$O(1)$ | Shortens string to new_len bytes (panics if new_len is not on a char boundary) |
s.clear() |
fn clear(&mut self) |
$O(1)$ | Sets len = 0 while keeping allocated capacity |
Removing Specific Characters with .pop() and .remove()
fn main() {
let mut code = String::from("#A9F!");
// 1. Remove the last character in O(1):
let last = code.pop();
println!("Popped: {last:?}, remaining: '{code}'");
// 2. Remove the first character at byte index 0 (shifts "A9F" left by 1 byte):
let first = code.remove(0);
println!("Removed at 0: '{first}', remaining: '{code}'");
}
Popped: Some('!'), remaining: '#A9F'
Removed at 0: '#', remaining: 'A9F'
Linear-Time In-Place Filtering with .retain()
Calling s.remove(idx) repeatedly in a loop is $O(n^2)$ because every removal shifts the tail of the string. By contrast, s.retain(|ch| ...) filters the entire String in place in $O(n)$ time with zero heap allocations:
fn main() {
let mut phone = String::from("+1 (800) 555-0199");
// Keep only ASCII digits in place (O(n) single pass, no reallocation):
phone.retain(|ch| ch.is_ascii_digit());
println!("Digits only: {phone}");
}
Digits only: 18005550199