269: Splitting by Predicate with partition()
Functional Programming
Tutorial
The Problem
Classifying elements into exactly two groups — evens and odds, valid and invalid, passing and failing — is extremely common. The naive approach iterates twice: once to collect matches, once to collect non-matches. The partition() adapter solves this by splitting an iterator into two collections in a single pass, reducing both computation time and code verbosity. This appears constantly in data validation, input parsing, and batch processing pipelines.
🎯 Learning Outcomes
partition(pred) as a single-pass split into two collections based on a predicatepartition() is more efficient than two separate filter() callspartition() to separate Result::is_ok from Result::is_err in a collection of resultspartition() with type annotations to clarify output collection typesCode Example
#![allow(clippy::all)]
//! 269. Splitting by predicate with partition()
//!
//! `partition(pred)` splits an iterator into two collections in a single pass.
#[cfg(test)]
mod tests {
#[test]
fn test_partition_even_odd() {
let (evens, odds): (Vec<i32>, Vec<i32>) = (1..=6).partition(|&x| x % 2 == 0);
assert_eq!(evens, vec![2, 4, 6]);
assert_eq!(odds, vec![1, 3, 5]);
}
#[test]
fn test_partition_results() {
let v: Vec<Result<i32, i32>> = vec![Ok(1), Err(2), Ok(3)];
let (oks, errs): (Vec<_>, Vec<_>) = v.into_iter().partition(Result::is_ok);
assert_eq!(oks.len(), 2);
assert_eq!(errs.len(), 1);
}
#[test]
fn test_partition_all_true() {
let (yes, no): (Vec<i32>, Vec<i32>) = [2i32, 4, 6].iter().copied().partition(|&x| x > 0);
assert_eq!(yes.len(), 3);
assert!(no.is_empty());
}
}Key Differences
List.partition in OCaml and Iterator::partition() in Rust are semantically identical — same name, same behavior.filter() calls would require two passes.partition(Result::is_ok) then unwrap() — but partition_map from the itertools crate offers a cleaner API.OCaml Approach
OCaml provides List.partition which is exactly equivalent:
let (evens, odds) = List.partition (fun x -> x mod 2 = 0) [1;2;3;4;5;6]
(* evens = [2;4;6], odds = [1;3;5] *)
This is a standard library function in OCaml, making it one of the cleaner parallels between the two languages.
Full Source
#![allow(clippy::all)]
//! 269. Splitting by predicate with partition()
//!
//! `partition(pred)` splits an iterator into two collections in a single pass.
#[cfg(test)]
mod tests {
#[test]
fn test_partition_even_odd() {
let (evens, odds): (Vec<i32>, Vec<i32>) = (1..=6).partition(|&x| x % 2 == 0);
assert_eq!(evens, vec![2, 4, 6]);
assert_eq!(odds, vec![1, 3, 5]);
}
#[test]
fn test_partition_results() {
let v: Vec<Result<i32, i32>> = vec![Ok(1), Err(2), Ok(3)];
let (oks, errs): (Vec<_>, Vec<_>) = v.into_iter().partition(Result::is_ok);
assert_eq!(oks.len(), 2);
assert_eq!(errs.len(), 1);
}
#[test]
fn test_partition_all_true() {
let (yes, no): (Vec<i32>, Vec<i32>) = [2i32, 4, 6].iter().copied().partition(|&x| x > 0);
assert_eq!(yes.len(), 3);
assert!(no.is_empty());
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_partition_even_odd() {
let (evens, odds): (Vec<i32>, Vec<i32>) = (1..=6).partition(|&x| x % 2 == 0);
assert_eq!(evens, vec![2, 4, 6]);
assert_eq!(odds, vec![1, 3, 5]);
}
#[test]
fn test_partition_results() {
let v: Vec<Result<i32, i32>> = vec![Ok(1), Err(2), Ok(3)];
let (oks, errs): (Vec<_>, Vec<_>) = v.into_iter().partition(Result::is_ok);
assert_eq!(oks.len(), 2);
assert_eq!(errs.len(), 1);
}
#[test]
fn test_partition_all_true() {
let (yes, no): (Vec<i32>, Vec<i32>) = [2i32, 4, 6].iter().copied().partition(|&x| x > 0);
assert_eq!(yes.len(), 3);
assert!(no.is_empty());
}
}
Exercises
Path::exists() as the predicate.Vec<&str> of numbers, using partition() to simultaneously collect successfully parsed integers and the strings that failed to parse.