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

095: Double Ended

Difficulty: Intermediate Category: Iterators Concept: `next_back()` for double-ended iteration Key Insight: DoubleEndedIterator iterates from both ends โ€” enables `rev()` without collecting first
// 095: Double-Ended Iterator

fn is_palindrome(v: &[i32]) -> bool {
    v.iter().eq(v.iter().rev())
}

fn take_from_both(v: &[i32]) -> (Vec<i32>, Vec<i32>) {
    let mut iter = v.iter();
    let front: Vec<i32> = (0..2).filter_map(|_| iter.next().copied()).collect();
    let back: Vec<i32> = (0..2).filter_map(|_| iter.next_back().copied()).collect();
    (front, back)
}

fn main() {
    println!("palindrome [1,2,3,2,1]: {}", is_palindrome(&[1, 2, 3, 2, 1]));
    let (f, b) = take_from_both(&[1, 2, 3, 4, 5]);
    println!("front: {:?}, back: {:?}", f, b);

    // rev() works because slice iterator is DoubleEnded
    let reversed: Vec<i32> = [1, 2, 3, 4, 5].iter().rev().copied().collect();
    println!("reversed: {:?}", reversed);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_palindrome() {
        assert!(is_palindrome(&[1, 2, 3, 2, 1]));
        assert!(!is_palindrome(&[1, 2, 3]));
    }

    #[test]
    fn test_take_both() {
        let (f, b) = take_from_both(&[1, 2, 3, 4, 5]);
        assert_eq!(f, vec![1, 2]);
        assert_eq!(b, vec![5, 4]);
    }

    #[test]
    fn test_next_back() {
        let v = vec![1, 2, 3];
        let mut iter = v.iter();
        assert_eq!(iter.next_back(), Some(&3));
        assert_eq!(iter.next(), Some(&1));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), None);
    }
}
(* 095: Double-Ended Iteration *)
(* OCaml lists are singly-linked โ€” no efficient reverse iteration *)
(* We simulate with arrays *)

let iter_both arr =
  let n = Array.length arr in
  let front = ref 0 in
  let back = ref (n - 1) in
  let next_front () =
    if !front > !back then None
    else (let v = arr.(!front) in incr front; Some v)
  in
  let next_back () =
    if !back < !front then None
    else (let v = arr.(!back) in decr back; Some v)
  in
  (next_front, next_back)

let is_palindrome_arr arr =
  let n = Array.length arr in
  let rec check i = i >= n / 2 || (arr.(i) = arr.(n - 1 - i) && check (i + 1)) in
  check 0

(* Tests *)
let () =
  let arr = [|1; 2; 3; 4; 5|] in
  let (nf, nb) = iter_both arr in
  assert (nf () = Some 1);
  assert (nb () = Some 5);
  assert (nf () = Some 2);
  assert (nb () = Some 4);
  assert (is_palindrome_arr [|1; 2; 3; 2; 1|]);
  assert (not (is_palindrome_arr [|1; 2; 3|]));
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Core Insight

DoubleEndedIterator iterates from both ends โ€” enables `rev()` without collecting first

OCaml Approach

  • See example.ml for implementation

Rust Approach

  • See example.rs for implementation

Comparison Table

FeatureOCamlRust
Seeexample.mlexample.rs