How to retain elements in Vec

Vec::retain filters a vector in place using a closure, with no extra allocation. Learn how it differs from filter().collect(), when to use retain_mut, and when to reach for extract_if.

The "remove the bad ones" problem

You've got a Vec<Order> with a thousand orders. You need to throw away every cancelled one and keep the rest. The instinct from Python is something like:

orders = [o for o in orders if not o.cancelled]

That's a list comprehension; it builds a new list. In Rust, you have a few options. You can do the same kind of thing with iter().filter().collect(), allocating a new Vec. Or you can mutate the existing Vec in place, deleting the cancelled ones and shifting the survivors down. The standard library's name for the second operation is retain.

let mut v = vec![1, 2, 3, 4, 5];

// Keep only even numbers. The closure returns true for "keep this one."
v.retain(|&x| x % 2 == 0);

// v is now [2, 4]. The vector is the same allocation; capacity is unchanged.

The shape is "give me a function that returns true for keepers." Anything where the closure returns false gets removed. The order of the remaining elements is preserved.

Why retain instead of filter+collect

Both work. They have different costs.

filter().collect() creates a brand new Vec and copies the surviving elements into it. The original Vec is dropped (or kept, if you assigned the result somewhere new). For large vectors, this means doubling your memory use briefly, and it always allocates.

retain modifies the existing Vec in place. No new allocation, no extra memory peak. It walks the vector once, and elements that pass the predicate get shifted down to fill the gaps. The capacity stays the same; only the length shrinks.

That last bit matters for performance loops. If you're calling retain on the same vector many times in a row, the capacity stays large and you don't pay for repeated allocations. If you used filter().collect() in the same spot, you'd allocate a fresh buffer every iteration.

For a one-off filter, the difference doesn't matter. For a hot loop, it can.

A more realistic example

Here's a tiny inventory system. We periodically purge expired items.

use std::time::{SystemTime, Duration};

struct Item {
    name: String,
    expires_at: SystemTime,
}

fn purge_expired(items: &mut Vec<Item>, now: SystemTime) {
    // Closure captures `now`. retain keeps everything not yet expired.
    items.retain(|item| item.expires_at > now);
}

fn main() {
    let now = SystemTime::now();
    let mut items = vec![
        Item { name: "milk".into(),   expires_at: now - Duration::from_secs(3600) },
        Item { name: "bread".into(),  expires_at: now + Duration::from_secs(3600) },
        Item { name: "cheese".into(), expires_at: now + Duration::from_secs(86400) },
    ];

    purge_expired(&mut items, now);

    // Only bread and cheese survive; their order is preserved.
    for item in &items {
        println!("{}", item.name);
    }
}

The closure here is |item| item.expires_at > now. It takes a reference to each Item (notice no & on the parameter; for non-Copy types you usually pattern-match the reference directly). It returns bool. retain calls it once per element and keeps the ones it says yes to.

Mutating while retaining

There's a sibling method, retain_mut, that gives the closure a &mut T instead of &T. Useful when you want to both filter and rewrite at once.

let mut nums = vec![1, 2, 3, 4, 5];

// Drop odd numbers AND double the even ones, in a single pass.
nums.retain_mut(|n| {
    if *n % 2 == 0 {
        *n *= 2;
        true
    } else {
        false
    }
});

// nums is now [4, 8].

Without retain_mut, you'd need two passes: one to filter, one to mutate. Not a big deal in casual code, but retain_mut keeps it tight when both operations are tied together.

How retain works under the hood

You can think of retain as roughly this:

fn retain_demo<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, mut f: F) {
    let mut write = 0;
    for read in 0..v.len() {
        if f(&v[read]) {
            // Shift the kept element down to the write index, but only when
            // they differ (avoid pointless self-moves).
            if write != read {
                v.swap(read, write);
            }
            write += 1;
        }
    }
    // Truncate the tail. Anything beyond `write` is either a duplicate
    // (because of the swap) or a discarded element; truncate drops them.
    v.truncate(write);
}

The actual stdlib version is more careful about panic safety and uses unsafe code to avoid double-counting. But the principle is the same: walk the vector once with two indices, write keepers down toward the front, then truncate. That's why the original order is preserved and why no new allocation happens.

The closure runs exactly once per element. If your predicate is expensive, that's the dominant cost.

Common pitfalls

You wrote a closure that takes ownership of the element and got a confusing borrow error. retain passes references, not values:

error[E0507]: cannot move out of `*x` which is behind a shared reference

Fix: pattern-match the reference, e.g. |x| ... and use *x for Copy types, or |x| x.field == ... for struct fields.

You expected the closure signature to match Iterator::filter. Almost the same, but retain takes FnMut(&T) -> bool (no double reference) while iterator-filter takes FnMut(&Self::Item) -> bool which becomes &&T if you started with references. The two closures look identical for Vec<i32> because i32 is Copy; they look slightly different for Vec<&str>. If in doubt, let the compiler tell you the expected signature.

You tried to retain on a HashMap and didn't realise the closure signature is different. HashMap::retain is FnMut(&K, &mut V) -> bool. Different shape because you usually want to filter on key+value together.

You called retain on a Vec<T> thinking it would also free memory. It doesn't shrink the allocation. Use v.shrink_to_fit() afterwards if you want to release the unused capacity.

When to use retain vs alternatives

Use retain for in-place filtering of a Vec you already own and intend to keep using. No allocation, preserves capacity, fast.

Use iter().filter().collect() when you want a new collection (perhaps with a different element type), or when you want to chain other iterator adapters: vec.iter().filter(...).map(...).collect().

Use drain_filter (now extract_if on stable as of Rust 1.83+) when you want both: keep the survivors in the original Vec, but also get back the removed ones. retain discards the rejected elements; extract_if lets you do something with them.

Use HashMap::retain for the same idea on a hash map. Same shape, different closure signature.

For purely functional pipelines (no in-place mutation, you want a clean transformation), prefer the iterator chain. For "mutate this thing I already have," prefer retain.

See How to filter a Vec for the alternative iterator-based approach, and How to Filter a Vector in Rust for a broader comparison.

Where to go next

retain is one of the small, well-named operations that makes Rust's collection API feel polished. The bigger lesson is that the standard library gives you both the "build something new" and "modify in place" versions of most operations; pick whichever matches the lifecycle of your data.

How to filter a Vec

How to Filter a Vector in Rust

What Is the Entry API for HashMap in Rust?