Vec::pop_if¶
Level: reference · for working programmers
One line: Pop the last element, but only if it passes a test.
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— the unconditional versionVec::truncate— dropping a tail by length rather than by testVec::retain— the same test applied to every element