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

261: Lookahead with Peekable

Difficulty: 2 Level: Intermediate Inspect the next element of an iterator without consuming it โ€” essential for parsers and grouping algorithms.

The Problem This Solves

Sometimes the decision of what to do with the current element depends on what comes next. A tokenizer needs to know if the next character is a digit before deciding to start a number. A run-length encoder needs to know if the next element equals the current one before deciding to close a group. A merge algorithm needs to compare heads of two sorted streams. Without lookahead, you're forced to consume an element just to inspect it โ€” then you need to "put it back", which iterators don't support. The workaround is consuming into a buffer, peeking at index 0, and managing the buffer manually. That's exactly what `Peekable` does for you, but as a proper iterator wrapper. OCaml doesn't have peekable iterators in the standard library โ€” you typically carry a `ref` to the lookahead value, or restructure the algorithm. In Rust, `.peekable()` wraps any iterator and adds `peek()` in one call.

The Intuition

`peekable()` wraps an iterator. Calling `peek()` on it returns a reference to the next element without advancing the iterator. The next call to `next()` still returns that same element.
let mut iter = [1, 2, 3].iter().peekable();
iter.peek();   // โ†’ Some(&&1)  โ€” iterator still at position 0
iter.next();   // โ†’ Some(&1)   โ€” now consumed
iter.next();   // โ†’ Some(&2)

How It Works in Rust

// Group consecutive equal elements
let data = [1i32, 1, 2, 2, 2, 3, 1, 1];
let mut iter = data.iter().peekable();
let mut groups: Vec<Vec<i32>> = Vec::new();

while let Some(&val) = iter.peek() {
 let mut group = Vec::new();
 // consume elements while they equal the peeked value
 while iter.peek() == Some(&val) {
     group.push(*iter.next().unwrap());
 }
 groups.push(group);
}
// โ†’ [[1,1], [2,2,2], [3], [1,1]]

// Tokenizer: decide based on what's next
let input: Vec<char> = "123abc".chars().collect();
let mut it = input.iter().peekable();
let mut tokens: Vec<String> = Vec::new();

while it.peek().is_some() {
 if it.peek().unwrap().is_ascii_digit() {
     // collect a full run of digits without re-inserting
     let digits: String = std::iter::from_fn(|| {
         if it.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
             it.next().copied()
         } else { None }
     }).collect();
     tokens.push(format!("NUM:{}", digits));
 } else {
     tokens.push(format!("CHAR:{}", it.next().unwrap()));
 }
}
`peek()` returns `Option<&Self::Item>` โ€” a reference to the buffered element. For `iter()` over `&T`, that's `Option<&&T>`, so double-deref or use `copied()` when needed.

What This Unlocks

Key Differences

ConceptOCamlRust
LookaheadManual `ref` cell or restructure`.peekable()` + `.peek()`
Non-consuming inspectNot built-in for sequences`peek()` โ€” zero-cost buffering
Advance after peekManually manageNext `next()` call consumes the peeked element
`peek_mut()`N/AMutable reference to next element
//! 261. Lookahead with Peekable
//!
//! `Peekable` adds `peek()` to inspect the next element without consuming it.

fn main() {
    let mut iter = [1i32, 2, 3, 4, 5].iter().peekable();
    println!("Peek: {:?}", iter.peek());
    println!("Next: {:?}", iter.next());
    println!("Peek: {:?}", iter.peek());
    println!("Next: {:?}", iter.next());

    // Group consecutive equal elements
    let data = [1i32, 1, 2, 2, 2, 3, 1, 1];
    let mut iter = data.iter().peekable();
    let mut groups: Vec<Vec<i32>> = Vec::new();
    while let Some(&val) = iter.peek() {
        let mut group = Vec::new();
        while iter.peek() == Some(&val) {
            group.push(*iter.next().unwrap());
        }
        groups.push(group);
    }
    println!("Groups: {:?}", groups);

    // Tokenizer: parse runs of digits
    let input: Vec<char> = "123abc456".chars().collect();
    let mut it = input.iter().peekable();
    let mut tokens: Vec<String> = Vec::new();
    while it.peek().is_some() {
        if it.peek().unwrap().is_ascii_digit() {
            let digits: String = std::iter::from_fn(|| {
                if it.peek().map(|c| c.is_ascii_digit()).unwrap_or(false) {
                    it.next().copied()
                } else { None }
            }).collect();
            tokens.push(format!("NUM:{}", digits));
        } else {
            tokens.push(format!("CHAR:{}", it.next().unwrap()));
        }
    }
    println!("Tokens: {:?}", tokens);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_peek_no_consume() {
        let mut iter = [1i32, 2, 3].iter().peekable();
        let p = iter.peek().copied().copied();
        let n = iter.next().copied();
        assert_eq!(p, Some(1));
        assert_eq!(n, Some(1));
        assert_eq!(iter.next().copied(), Some(2));
    }

    #[test]
    fn test_peek_groups() {
        let data = [1i32, 1, 2, 3, 3];
        let mut iter = data.iter().peekable();
        let mut groups: Vec<Vec<i32>> = Vec::new();
        while let Some(&val) = iter.peek() {
            let mut g = Vec::new();
            while iter.peek() == Some(&val) { g.push(*iter.next().unwrap()); }
            groups.push(g);
        }
        assert_eq!(groups, vec![vec![1,1], vec![2], vec![3,3]]);
    }

    #[test]
    fn test_peek_empty() {
        let data: Vec<i32> = vec![];
        let mut p = data.iter().peekable();
        assert_eq!(p.peek(), None);
    }
}
(* 261. Lookahead with Peekable - OCaml *)

type 'a peekable = {
  mutable peeked: 'a option;
  mutable rest: 'a list;
}

let make_peekable lst = { peeked = None; rest = lst }

let peek p =
  match p.peeked with
  | Some _ as v -> v
  | None ->
    (match p.rest with
     | [] -> None
     | x :: _ -> p.peeked <- Some x; Some x)

let next p =
  match p.peeked with
  | Some v -> p.peeked <- None; (match p.rest with _ :: xs -> p.rest <- xs | [] -> ()); Some v
  | None ->
    match p.rest with
    | [] -> None
    | x :: xs -> p.rest <- xs; Some x

let () =
  let p = make_peekable [1; 2; 3; 4; 5] in
  Printf.printf "Peek: %s\n" (match peek p with Some n -> string_of_int n | None -> "None");
  Printf.printf "Next: %s\n" (match next p with Some n -> string_of_int n | None -> "None");
  Printf.printf "Next: %s\n" (match next p with Some n -> string_of_int n | None -> "None");
  let p2 = make_peekable [1; 1; 2; 2; 3] in
  let rec group_eq () =
    match next p2 with
    | None -> []
    | Some x ->
      let rec collect acc =
        match peek p2 with
        | Some y when y = x -> ignore (next p2); collect (x :: acc)
        | _ -> List.rev (x :: acc)
      in
      collect [] :: group_eq ()
  in
  let groups = group_eq () in
  List.iter (fun g ->
    Printf.printf "[%s] " (String.concat ";" (List.map string_of_int g))
  ) groups;
  print_newline ()