๐Ÿฆ€ Functional Rust
๐ŸŽฌ How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Iterators are lazy โ€” .map(), .filter(), .take() build a chain but do no work until consumed

โ€ข .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

โ€ข Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

โ€ข .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

โ€ข Chaining replaces nested loops with a readable, composable pipeline

269: Splitting by Predicate with partition()

Difficulty: 1 Level: Beginner Split an iterator into two collections โ€” matching and non-matching โ€” in a single pass.

The Problem This Solves

You want to separate a list into two groups: the elements that satisfy a condition and those that don't. Evens and odds. Valid and invalid inputs. `Ok` results and `Err` results. The naive approach runs `filter` twice โ€” once for each half โ€” iterating the source twice and making the intent less clear. Or you write a loop with two `push` calls and a conditional. `partition()` does both halves in one pass, returning two collections simultaneously. It's both more efficient (single iteration) and more expressive (the split intent is explicit). In Python, you'd use two comprehensions or `itertools.partition`. In OCaml, `List.partition`. In Rust, `partition()` is available on any iterator.

The Intuition

`partition(pred)` returns `(matching, not_matching)` โ€” two collections where every element from the iterator ends up in exactly one of them.
let (evens, odds): (Vec<i32>, Vec<i32>) =
 (1..=10).partition(|&x| x % 2 == 0);
// evens โ†’ [2, 4, 6, 8, 10]
// odds  โ†’ [1, 3, 5, 7, 9]

How It Works in Rust

// Separate evens and odds
let nums: Vec<i32> = (1..=10).collect();
let (evens, odds): (Vec<i32>, Vec<i32>) =
 nums.iter().copied().partition(|&x| x % 2 == 0);

// Separate by string length
let words = ["hi", "hello", "yo", "world", "hey", "programming"];
let (short, long): (Vec<_>, Vec<_>) = words.iter().partition(|w| w.len() <= 3);

// Separate Ok and Err results (very common pattern)
let results: Vec<Result<i32, &str>> = vec![
 Ok(1), Err("bad"), Ok(3), Err("fail"), Ok(5)
];
let (oks, errs): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
// Then extract values:
let ok_vals: Vec<i32> = oks.into_iter().flatten().collect();
let err_msgs: Vec<&str> = errs.into_iter().map(|r| r.unwrap_err()).collect();

// Numeric sign splitting
let data = [-3i32, 1, -1, 4, -1, 5, 9, -2, 6];
let (pos, neg): (Vec<i32>, Vec<i32>) =
 data.iter().copied().partition(|&x| x >= 0);
println!("Sum pos: {}, Sum neg: {}", pos.iter().sum::<i32>(), neg.iter().sum::<i32>());
Unlike `take_while`/`skip_while`, `partition` scans the entire iterator โ€” it does not stop early. Every element is evaluated and placed.

What This Unlocks

Key Differences

ConceptOCamlRust
Split by predicate`List.partition pred lst``iter.partition(pred)`
Scans entire iteratorYesYes โ€” no early termination
vs. `take_while`/`filter``partition` keeps both halves`take_while` stops; `filter` drops one half
Collection typeAlways `list * list`Generic โ€” type annotation required
//! 269. Splitting by predicate with partition()
//!
//! `partition(pred)` splits an iterator into two collections in a single pass.

fn main() {
    let nums: Vec<i32> = (1..=10).collect();
    let (evens, odds): (Vec<i32>, Vec<i32>) =
        nums.iter().copied().partition(|&x| x % 2 == 0);
    println!("Evens: {:?}", evens);
    println!("Odds:  {:?}", odds);

    let words = ["hi", "hello", "yo", "world", "hey", "programming"];
    let (short, long): (Vec<_>, Vec<_>) = words.iter().partition(|w| w.len() <= 3);
    println!("Short: {:?}", short);
    println!("Long:  {:?}", long);

    let results: Vec<Result<i32, &str>> = vec![
        Ok(1), Err("bad"), Ok(3), Err("fail"), Ok(5)
    ];
    let (oks, errs): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
    let ok_vals: Vec<i32> = oks.into_iter().flatten().collect();
    let err_msgs: Vec<&str> = errs.into_iter().map(|r| r.unwrap_err()).collect();
    println!("Ok values: {:?}", ok_vals);
    println!("Err values: {:?}", err_msgs);

    let data = [-3i32, 1, -1, 4, -1, 5, 9, -2, 6];
    let (pos, neg): (Vec<i32>, Vec<i32>) =
        data.iter().copied().partition(|&x| x >= 0);
    println!("Sum pos: {}, Sum neg: {}", pos.iter().sum::<i32>(), neg.iter().sum::<i32>());
}

#[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());
    }
}
(* 269. Splitting by predicate with partition() - OCaml *)

let () =
  let nums = List.init 10 (fun i -> i + 1) in
  let (evens, odds) = List.partition (fun x -> x mod 2 = 0) nums in
  Printf.printf "Evens: %s\n" (String.concat ", " (List.map string_of_int evens));
  Printf.printf "Odds:  %s\n" (String.concat ", " (List.map string_of_int odds));

  let words = ["hi"; "hello"; "yo"; "world"; "hey"; "programming"] in
  let (short, long) = List.partition (fun w -> String.length w <= 3) words in
  Printf.printf "Short: %s\n" (String.concat ", " short);
  Printf.printf "Long: %s\n" (String.concat ", " long);

  let data = [-3; 1; -1; 4; -1; 5; 9; -2; 6] in
  let (pos, neg) = List.partition (fun x -> x >= 0) data in
  Printf.printf "Sum pos: %d, Sum neg: %d\n"
    (List.fold_left (+) 0 pos)
    (List.fold_left (+) 0 neg)