Vec<T> in Depth

retain() and dedup()

Vec<T> provides two linear-time ($O(n)$), in-place compaction families that remove unwanted elements in a single pass without reallocating the vector: .retain() / .retain_mut() for predicate filtering, and .dedup() / .dedup_by() / .dedup_by_key() for collapsing consecutive duplicates.

In-Place Filtering with retain() and retain_mut()

impl<T> Vec<T> {
    pub fn retain<F>(&mut self, f: F)
    where
        F: FnMut(&T) -> bool;

    pub fn retain_mut<F>(&mut self, f: F)
    where
        F: FnMut(&mut T) -> bool;
}
  • v.retain(f) visits each element in order (0..len), drops elements where f(&elem) returns false, and shifts kept elements left in a single $O(n)$ pass.
  • v.retain_mut(f) passes &mut T to the closure so you can both mutate kept elements in place and decide whether to keep them in one pass:
fn main() {
    let mut timers = vec![3_u32, 1, 4, 1, 2];

    // Decrement each timer by 1 and keep only timers that have not reached 0:
    timers.retain_mut(|t| {
        *t -= 1;
        *t > 0
    });

    println!("Active timers after tick: {timers:?}");
}
Active timers after tick: [2, 3, 1]

Collapsing Consecutive Duplicates with dedup(), dedup_by(), and dedup_by_key()

impl<T: PartialEq> Vec<T> {
    pub fn dedup(&mut self);
}

impl<T> Vec<T> {
    pub fn dedup_by<F>(&mut self, same_bucket: F)
    where
        F: FnMut(&mut T, &mut T) -> bool;

    pub fn dedup_by_key<F, K>(&mut self, key: F)
    where
        F: FnMut(&mut T) -> K,
        K: PartialEq;
}

Important: .dedup() only removes adjacent (consecutive) duplicates in $O(n)$ time. To remove all duplicates across an unsorted vector, sort the vector first (v.sort_unstable()) and then call v.dedup():

Unsorted `[1, 2, 1, 2]`:
  ├── v.dedup()                        ──> `[1, 2, 1, 2]` (no adjacent duplicates!)
  └── v.sort_unstable(); v.dedup();    ──> `[1, 2]`       (all duplicates removed)
fn main() {
    // 1. Removing consecutive duplicates only:
    let mut consecutive = vec![1, 1, 2, 3, 3, 3, 2, 2];
    consecutive.dedup();
    println!("Consecutive dedup: {consecutive:?}");

    // 2. Removing ALL duplicates by sorting first:
    let mut all_unique = vec![30, 10, 20, 30, 10];
    all_unique.sort_unstable();
    all_unique.dedup();
    println!("Sorted + dedup:    {all_unique:?}");

    // 3. Merging adjacent elements in place with `dedup_by`
    // (Note: in `dedup_by(|a, b| ...)`, `b` is the earlier kept element and `a` is the next element):
    let mut runs = vec![("cpu", 10), ("cpu", 15), ("mem", 40)];
    runs.dedup_by(|next, kept| {
        if next.0 == kept.0 {
            kept.1 += next.1;
            true // Drop `next` after accumulating its count into `kept`
        } else {
            false
        }
    });
    println!("Merged runs:       {runs:?}");
}
Consecutive dedup: [1, 2, 3, 2]
Sorted + dedup:    [10, 20, 30]
Merged runs:       [("cpu", 25), ("mem", 40)]