#043 Mar 25, 2026

43. Vec::extract_if — Remove Elements and Keep Them

Ever needed to split a Vec into two groups — the ones you keep and the ones you remove? retain discards the removed items. Now there’s a better way.

Vec::extract_if (stable since Rust 1.87) removes elements matching a predicate and hands them back as an iterator — in a single pass.

The old way — two passes, logic must stay in sync

1
2
3
4
5
6
7
8
9
let mut numbers = vec![1, 2, 3, 4, 5, 6];

// Collect the evens first…
let evens: Vec<i32> = numbers.iter().filter(|&&x| x % 2 == 0).copied().collect();
// …then remove them (predicate must match exactly)
numbers.retain(|&x| x % 2 != 0);

assert_eq!(numbers, [1, 3, 5]);
assert_eq!(evens,   [2, 4, 6]);

The filter and the retain predicates must be inverses of each other — easy to mistype, and you touch the data twice.

The new way — one pass, one predicate

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

let evens: Vec<i32> = numbers.extract_if(.., |&mut x| x % 2 == 0).collect();

assert_eq!(numbers, [1, 3, 5]);
assert_eq!(evens,   [2, 4, 6]);

extract_if walks the Vec, removes every element where the closure returns true, and yields it. The .. is a range — you can narrow it to only consider a slice of the vector.

Real-world example: draining a work queue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#[derive(Debug)]
struct Job { id: u32, priority: u8 }

let mut queue = vec![
    Job { id: 1, priority: 3 },
    Job { id: 2, priority: 9 },
    Job { id: 3, priority: 1 },
    Job { id: 4, priority: 8 },
];

// Pull out all high-priority jobs for immediate processing
let urgent: Vec<Job> = queue.extract_if(.., |j| j.priority >= 8).collect();

assert_eq!(urgent.len(), 2);  // jobs 2 and 4
assert_eq!(queue.len(),  2);  // jobs 1 and 3 remain

HashMap and HashSet also gained extract_if in Rust 1.88.

Note: The closure takes &mut T, so you can even mutate elements mid-extraction before deciding whether to remove them.

← Previous 42. array::from_fn — Build Arrays from a Function