098 — Partition Iterator
Tutorial
The Problem
Use Rust's .partition(predicate) to split an iterator into two collections in a single pass: elements satisfying the predicate go into the first collection, the rest into the second. Compare with OCaml's List.partition and partition_map using Either.
🎯 Learning Outcomes
iter.partition(|x| pred(x)) to produce two Vec<T> simultaneouslypartition consumes the iterator in one pass — no intermediate allocation(Vec<T>, Vec<T>) for the result destructuring.partition() to OCaml's List.partitionpartition_map for element-wise classification into two typed collectionsCode Example
#![allow(clippy::all)]
// 098: Partition Iterator
#[cfg(test)]
mod tests {
#[test]
fn test_partition() {
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_empty() {
let (a, b): (Vec<i32>, Vec<i32>) = std::iter::empty().partition(|_: &i32| true);
assert!(a.is_empty());
assert!(b.is_empty());
}
#[test]
fn test_partition_all_match() {
let (yes, no): (Vec<i32>, Vec<i32>) = (1..=3).partition(|_| true);
assert_eq!(yes, vec![1, 2, 3]);
assert!(no.is_empty());
}
}Key Differences
| Aspect | Rust | OCaml |
|---|---|---|
| Built-in | Iterator::partition(pred) | List.partition pred lst |
| Return type | (Vec<T>, Vec<T>) | 'a list * 'a list |
| Type annotation | Required on destructuring | Inferred |
partition_map | Custom or itertools | List.partition_map (OCaml 4.12+) |
| Single pass | Yes | Yes |
| Empty input | Returns two empty vecs | Returns ([], []) |
partition is cleaner than filter + filter for two complementary predicates — it avoids traversing the input twice. Use it whenever you need both sides of a classification simultaneously.
OCaml Approach
List.partition pred lst is built into the standard library and returns a tuple (list, list). partition_map f lst uses Either.Left/Either.Right to route elements to different typed collections via fold_right. OCaml's partition is lazy-safe only via Seq.partition in newer versions.
Full Source
#![allow(clippy::all)]
// 098: Partition Iterator
#[cfg(test)]
mod tests {
#[test]
fn test_partition() {
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_empty() {
let (a, b): (Vec<i32>, Vec<i32>) = std::iter::empty().partition(|_: &i32| true);
assert!(a.is_empty());
assert!(b.is_empty());
}
#[test]
fn test_partition_all_match() {
let (yes, no): (Vec<i32>, Vec<i32>) = (1..=3).partition(|_| true);
assert_eq!(yes, vec![1, 2, 3]);
assert!(no.is_empty());
}
}#[cfg(test)]
mod tests {
#[test]
fn test_partition() {
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_empty() {
let (a, b): (Vec<i32>, Vec<i32>) = std::iter::empty().partition(|_: &i32| true);
assert!(a.is_empty());
assert!(b.is_empty());
}
#[test]
fn test_partition_all_match() {
let (yes, no): (Vec<i32>, Vec<i32>) = (1..=3).partition(|_| true);
assert_eq!(yes, vec![1, 2, 3]);
assert!(no.is_empty());
}
}
Deep Comparison
Core Insight
Partition separates elements into two collections based on a predicate — like filter but keeps both sides
OCaml Approach
Rust Approach
Comparison Table
| Feature | OCaml | Rust |
|---|---|---|
| See | example.ml | example.rs |
Exercises
partition_map<T, A, B>(iter: impl Iterator<Item=T>, f: impl Fn(T) -> Result<A, B>) -> (Vec<A>, Vec<B>) using fold.partition to split a Vec<Result<T, E>> into (Vec<T>, Vec<E>).three_way_partition<T>(v: Vec<T>, pred1: impl Fn(&T)->bool, pred2: impl Fn(&T)->bool) -> (Vec<T>, Vec<T>, Vec<T>).partition(pred) is equivalent to (filter(pred).collect(), filter(|x| !pred(x)).collect()) and explain the trade-off.partition_fold that groups list elements into n buckets based on f: 'a -> int using Array of lists.