๐Ÿฆ€ 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

264: Conditional Stopping with take_while()

Difficulty: 1 Level: Beginner Consume elements from the front of an iterator until a condition fails โ€” lazy, works on infinite iterators.

The Problem This Solves

You have a sorted sequence or a stream and you want everything up to a boundary. Reading log entries until you hit a timestamp past midnight. Taking words until you hit an empty string. Consuming sorted numbers up to a threshold. With a for loop, you'd set a flag or `break` โ€” which works, but you lose the composability of the iterator chain. The critical distinction from `filter()` is ordering: `take_while` assumes elements are ordered with respect to the predicate. It stops at the first failure โ€” it doesn't scan the whole iterator. That makes it the only option for infinite iterators, where scanning everything is impossible. OCaml has `List.filteri` but not a built-in `take_while` for lists (though `Seq.take_while` exists). In Rust, `take_while(pred)` is available on any iterator and is lazy.

The Intuition

`take_while(pred)` yields elements as long as the predicate returns `true`. The moment it returns `false`, the iterator stops โ€” even if later elements would match.
let nums = [1, 2, 3, 4, 5, 4, 3];
let result: Vec<_> = nums.iter().take_while(|&&x| x < 4).collect();
// โ†’ [1, 2, 3]   stops at 4, never sees the trailing 3

How It Works in Rust

// Basic: take leading elements less than 5
let nums = [1i32, 2, 3, 4, 5, 6, 7, 8, 9];
let small: Vec<i32> = nums.iter().copied().take_while(|&x| x < 5).collect();
// โ†’ [1, 2, 3, 4]

// Stops at first failure โ€” NOT a filter
let data = [3i32, 1, 4, 1, -5, 9];
let positives: Vec<i32> = data.iter().copied().take_while(|&x| x > 0).collect();
// โ†’ [3, 1, 4, 1]    stops at -5; the 9 after it is never seen

// Works on infinite iterators โ€” essential use case
let triangulars: Vec<u64> = (1u64..)
 .take_while(|&n| n * (n + 1) / 2 < 30)
 .collect();
// โ†’ [1, 2, 3, 4, 5, 6, 7]   n where triangle number < 30

// Parse a leading alphabetic word from mixed input
let word: String = "hello123world".chars()
 .take_while(|c| c.is_alphabetic())
 .collect();
// โ†’ "hello"

// Sorted list: short words at the front
let sorted_words = ["ant", "bee", "cat", "dog", "elephant"];
let short: Vec<_> = sorted_words.iter()
 .take_while(|w| w.len() <= 3)
 .collect();
// โ†’ ["ant", "bee", "cat", "dog"]
Combine with `skip_while` to extract a middle range: skip a prefix, then take until the end condition.

What This Unlocks

Key Differences

ConceptOCamlRust
Take prefix by predicate`Seq.take_while` (lazy) / manual for lists`iter.take_while(pred)`
Stops at first failureYes (same semantics)Yes
Works on infinite sequences`Seq` onlyAny `Iterator`
Combined with skipManual composition`.skip_while(p1).take_while(p2)`
//! 264. Conditional stopping with take_while()
//!
//! `take_while(pred)` yields elements until the predicate first returns false.

fn main() {
    let nums = [1i32, 2, 3, 4, 5, 6, 7, 8, 9];

    let small: Vec<i32> = nums.iter().copied().take_while(|&x| x < 5).collect();
    println!("Less than 5: {:?}", small);

    let data = [3i32, 1, 4, 1, -5, 9, -2, 6];
    let positives: Vec<i32> = data.iter().copied().take_while(|&x| x > 0).collect();
    println!("Leading positives: {:?}", positives);

    // Works on infinite iterators
    let triangulars: Vec<u64> = (1u64..)
        .take_while(|&n| n * (n + 1) / 2 < 30)
        .collect();
    println!("n where triangular(n)<30: {:?}", triangulars);

    let input = "hello123world";
    let word: String = input.chars().take_while(|c| c.is_alphabetic()).collect();
    println!("Leading word: '{}'", word);

    let sorted_words = ["ant", "bear", "cat", "dog", "elephant"];
    let short: Vec<_> = sorted_words.iter().take_while(|w| w.len() <= 3).collect();
    println!("Short words: {:?}", short);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_take_while_basic() {
        let result: Vec<i32> = [1, 2, 3, 4, 5].iter().copied()
            .take_while(|&x| x < 4).collect();
        assert_eq!(result, vec![1, 2, 3]);
    }

    #[test]
    fn test_take_while_none_match() {
        let result: Vec<i32> = [-1i32, 2, 3].iter().copied()
            .take_while(|&x| x > 0).collect();
        assert!(result.is_empty());
    }

    #[test]
    fn test_take_while_stops_early() {
        let result: Vec<i32> = [1i32, 2, 5, 1, 2].iter().copied()
            .take_while(|&x| x < 3).collect();
        assert_eq!(result, vec![1, 2]);
    }

    #[test]
    fn test_take_while_infinite() {
        let result: Vec<u32> = (0u32..).take_while(|&x| x < 5).collect();
        assert_eq!(result, vec![0, 1, 2, 3, 4]);
    }
}
(* 264. Conditional stopping with take_while() - OCaml *)

let rec take_while pred = function
  | [] -> []
  | x :: xs -> if pred x then x :: take_while pred xs else []

let () =
  let nums = [1; 2; 3; 4; 5; 6; 7; 8; 9] in
  let small = take_while (fun x -> x < 5) nums in
  Printf.printf "Less than 5: %s\n"
    (String.concat ", " (List.map string_of_int small));

  let data = [3; 1; 4; 1; -5; 9; -2; 6] in
  let positives = take_while (fun x -> x > 0) data in
  Printf.printf "Leading positives: %s\n"
    (String.concat ", " (List.map string_of_int positives));

  let naturals = List.init 100 (fun i -> i + 1) in
  let triangular = take_while (fun n -> n * (n+1) / 2 < 30) naturals in
  Printf.printf "n where triangular(n) < 30: %s\n"
    (String.concat ", " (List.map string_of_int triangular))