Skip to content

Vec::pop_if

Vec methods · Collections

Level: reference · for working programmers

One line: Pop the last element, but only if it passes a test.

pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T>

Stable since 1.86.0.

Returns None — and leaves the element in place — when the predicate says no. On an empty vector the predicate is never called at all.

The predicate takes &mut T, so it may change the element it then decides to keep. That is occasionally what you want and otherwise a surprise worth knowing about.

The shape it replaces is if v.last().is_some_and(cond) { v.pop() }, which reaches for the last element twice. while v.pop_if(cond).is_some() {} is the tidy way to trim a suffix.

Stable since 1.86.0. clippy::manual_pop_if rewrites the older two-step form into this one.

Example

vec_pop_if.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

fn main() {
    // Pops only if the last element passes the test. Nothing is removed
    // otherwise, and the element is left where it was.
    let mut v = vec![1, 2, 3, 4];
    println!("{:?} then {v:?}", v.pop_if(|n| *n % 2 == 0));
    println!("{:?} then {v:?}", v.pop_if(|n| *n % 2 == 0));

    // On an empty vector the predicate is never called.
    let mut calls = 0;
    let mut empty: Vec<i32> = Vec::new();
    let got = empty.pop_if(|_| { calls += 1; true });
    println!("empty: got {got:?}, predicate called {calls} times");

    // The predicate gets &mut, so it can change the element it decides to keep.
    let mut v = vec![String::from("ada")];
    let popped = v.pop_if(|s| { s.push('!'); s.len() > 99 });
    println!("kept and mutated: {popped:?} {v:?}");

    // Trimming a suffix without a loop-with-a-break.
    let mut trailing = vec![1, 2, 0, 0, 0];
    while trailing.pop_if(|n| *n == 0).is_some() {}
    println!("zeros trimmed: {trailing:?}");

    // The pre-1.86 spelling took two steps and a second bounds check.
    let mut v = vec![5, 6];
    if v.last().is_some_and(|n| *n > 5) { v.pop(); }
    println!("the old way: {v:?}");
}

Verified output of vec_pop_if.rs — regenerated by tools/run_examples.py, never hand-typed.

Some(4) then [1, 2, 3]
None then [1, 2, 3]
empty: got None, predicate called 0 times
kept and mutated: None ["ada!"]
zeros trimmed: [1, 2]
the old way: [5]

See also

Vec::pop_if in the standard library ↗