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

256: Chaining Iterators with chain()

Difficulty: 1 Level: Beginner Combine multiple sequences into one seamless iterator โ€” lazily, with zero extra allocation.

The Problem This Solves

You have two lists and you want to process them together: all the items from list A, then all the items from list B. The obvious approach is to create a new combined list โ€” but that allocates memory just to iterate. For large datasets, that's wasteful. Maybe you want to iterate over the default config values followed by the user-provided overrides. Or process log entries from multiple files. Or combine the results of two database queries. The naive approach concatenates them into a new `Vec` first, but you only need the combination long enough to iterate once. Rust's `.chain()` is the elegant solution: it creates a single iterator that goes through the first sequence, then the second, without allocating any storage for the combined result. The two original iterators stay exactly where they are; `.chain()` just connects them. This is a key example of how Rust iterators are lazy โ€” they describe what to do, not the result.

The Intuition

In Python you'd use `itertools.chain()` for exactly this:
from itertools import chain
combined = list(chain([1, 2, 3], [4, 5, 6]))
In JavaScript you might use spread syntax: `[...arr1, ...arr2]` โ€” but that creates a new array immediately. Rust's `.chain()` is closer to Python's `itertools.chain`: nothing is materialized until you ask for it. You have to call `.collect()` (or loop with `for`) to actually run the computation:
// Nothing runs yet โ€” this just describes the computation
let lazy = first.iter().chain(second.iter());

// NOW it runs โ€” both iterators are consumed in sequence
let combined: Vec<i32> = lazy.collect();

How It Works in Rust

let first  = [1, 2, 3];
let second = [4, 5, 6];

// .chain() connects two iterators โ€” lazy, no allocation
let chained: Vec<i32> = first.iter()
 .chain(second.iter())
 .copied()    // turn &i32 into i32 (copy the value out of the reference)
 .collect();  // THIS is when the work actually happens
// [1, 2, 3, 4, 5, 6]

// Works with any iterators โ€” not just slices
let greetings = vec!["hello", "hi", "hey"];
let farewells = vec!["bye", "goodbye", "ciao"];
let all: Vec<_> = greetings.iter().chain(farewells.iter()).collect();

// Works with computed iterators too
let evens = (0..10i32).filter(|x| x % 2 == 0);
let odds  = (0..10i32).filter(|x| x % 2 != 0);
let combined: Vec<i32> = evens.chain(odds).collect();
// [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]

// Chain three iterators by chaining twice
let a = vec![1i32];
let b = vec![2i32];
let c = vec![3i32];
let abc: Vec<i32> = a.into_iter().chain(b).chain(c).collect();
// [1, 2, 3]

// chain() is just an iterator adapter โ€” you can keep applying more operations
let sum: i32 = first.iter().chain(second.iter()).copied().sum();
// 21 โ€” no Vec ever created
Note `into_iter()` vs `.iter()`: `.iter()` borrows the collection (giving `&T`), `into_iter()` consumes it (giving `T`). For the chain with three `Vec`s above, we use `into_iter()` because the Vecs are consumed into the chain.

What This Unlocks

Key Differences

ConceptOCamlRust
Concatenate lists`List.append xs ys` or `xs @ ys``xs.iter().chain(ys.iter())`
LazinessEager (allocates immediately)Lazy โ€” nothing runs until consumed
Memory costNew list allocatedZero allocation in the iterator
Result type`'a list``Chain<IterA, IterB>` โ€” a new iterator type
MaterializeAlready materialized`.collect()` when you need a `Vec`
//! 256. Chaining iterators with chain()
//!
//! `chain()` concatenates two iterators lazily โ€” no allocation, just composition.

fn main() {
    let first = [1, 2, 3];
    let second = [4, 5, 6];
    let chained: Vec<i32> = first.iter().chain(second.iter()).copied().collect();
    println!("Chained: {:?}", chained);

    let greetings = vec!["hello", "hi", "hey"];
    let farewells = vec!["bye", "goodbye", "ciao"];
    let all: Vec<_> = greetings.iter().chain(farewells.iter()).collect();
    println!("All words: {:?}", all);

    let evens = (0..10i32).filter(|x| x % 2 == 0);
    let odds  = (0..10i32).filter(|x| x % 2 != 0);
    let combined: Vec<i32> = evens.chain(odds).collect();
    println!("Evens then odds: {:?}", combined);

    // Chain three iterators
    let a = vec![1i32];
    let b = vec![2i32];
    let c = vec![3i32];
    let abc: Vec<i32> = a.into_iter().chain(b).chain(c).collect();
    println!("Three-way chain: {:?}", abc);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_chain_basic() {
        let a = [1i32, 2, 3];
        let b = [4i32, 5, 6];
        let result: Vec<i32> = a.iter().chain(b.iter()).copied().collect();
        assert_eq!(result, vec![1, 2, 3, 4, 5, 6]);
    }

    #[test]
    fn test_chain_empty() {
        let a: Vec<i32> = vec![];
        let b = vec![1, 2];
        let result: Vec<i32> = a.iter().chain(b.iter()).copied().collect();
        assert_eq!(result, vec![1, 2]);
    }

    #[test]
    fn test_chain_count() {
        let a = [1i32, 2, 3];
        let b = [4i32, 5];
        assert_eq!(a.iter().chain(b.iter()).count(), 5);
    }
}
(* 256. Chaining iterators with chain() - OCaml *)
(* OCaml uses @ or List.append to concatenate lists eagerly *)

let () =
  let first = [1; 2; 3] in
  let second = [4; 5; 6] in
  (* @ operator is syntactic sugar for List.append -- allocates a new list *)
  let chained = first @ second in
  List.iter (fun x -> Printf.printf "%d " x) chained;
  print_newline ();

  let words_a = ["hello"; "world"] in
  let words_b = ["foo"; "bar"] in
  let all_words = List.append words_a words_b in
  List.iter (fun w -> Printf.printf "%s " w) all_words;
  print_newline ();

  (* Simulating lazy chaining with Seq module *)
  let seq1 = List.to_seq [10; 20; 30] in
  let seq2 = List.to_seq [40; 50; 60] in
  let chained_seq = Seq.append seq1 seq2 in
  Seq.iter (fun x -> Printf.printf "%d " x) chained_seq;
  print_newline ()