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

263: Fixed-Size Chunks Iteration

Difficulty: 2 Level: Intermediate Divide a slice into non-overlapping groups of N โ€” batch processing, pagination, matrix rows.

The Problem This Solves

You have a flat buffer and you want to process it in fixed-size batches: send 100 records to a database at a time, render a flat pixel array as image rows, paginate a list of results. The naive approach uses index arithmetic โ€” `data[in..(i+1)n]` โ€” which requires manually computing the number of batches and handling the final short batch. Unlike `windows()`, chunks are non-overlapping: each element appears in exactly one chunk. That makes them the right tool for batch operations where processing the same element twice would be wrong or wasteful. OCaml has no built-in chunk iterator. You'd write a recursive function that splits the list at position `n` repeatedly. In Rust, `chunks(n)` is a zero-copy slice method. The last chunk may be shorter than `n` if the length isn't evenly divisible โ€” `chunks_exact(n)` gives you only full-size chunks, with the remainder accessible separately.

The Intuition

`chunks(n)` divides the slice into consecutive non-overlapping sub-slices of at most `n` elements each. Unlike `windows()` which slides forward by 1, `chunks()` advances by the full window size.
let data = [1, 2, 3, 4, 5, 6, 7];
for chunk in data.chunks(3) {
 println!("{:?}", chunk);  // [1,2,3], [4,5,6], [7]
}

How It Works in Rust

let data = [1i32, 2, 3, 4, 5, 6, 7];

// Sum each chunk
let chunk_sums: Vec<i32> = data.chunks(3)
 .map(|c| c.iter().sum())
 .collect();
// โ†’ [6, 15, 7]

// chunks_exact: only full chunks, remainder accessible
let exact_iter = data.chunks_exact(3);
let remainder = exact_iter.remainder();  // โ†’ &[7]
let full_chunks: Vec<_> = data.chunks_exact(3).collect();
// โ†’ [[1,2,3], [4,5,6]]  (no partial chunk)

// Batch processing with index
let items: Vec<i32> = (1..=10).collect();
for (batch_num, batch) in items.chunks(4).enumerate() {
 println!("Batch {}: {:?}", batch_num, batch);
}

// Treat flat array as a 2D matrix (3 columns)
let matrix: Vec<i32> = (1..=9).collect();
for row in matrix.chunks(3) {
 println!("{:?}", row);  // [1,2,3], [4,5,6], [7,8,9]
}
Use `chunks_exact()` when a short final chunk would be a bug (e.g., parsing fixed-width binary records). Use `chunks()` when a short tail is acceptable.

What This Unlocks

Key Differences

ConceptOCamlRust
Non-overlapping groupsManual recursion`slice.chunks(n)`
Require exact sizeException / manual check`chunks_exact(n)` + `.remainder()`
Zero-copyNoYes โ€” references into original slice
Overlapping variantN/A`windows(n)`
Works on iteratorsN/ASlice method โ€” collect first if needed
//! 263. Fixed-size chunks iteration
//!
//! `chunks(n)` splits a slice into non-overlapping sub-slices of at most n elements.

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

    println!("Chunks of 3:");
    for chunk in data.chunks(3) {
        println!("  {:?} (len={})", chunk, chunk.len());
    }

    let chunk_sums: Vec<i32> = data.chunks(3).map(|c| c.iter().sum()).collect();
    println!("Chunk sums: {:?}", chunk_sums);

    // chunks_exact: only full chunks, with remainder
    let exact = data.chunks_exact(3);
    let remainder = exact.remainder();
    println!("Exact chunks of 3:");
    for chunk in data.chunks_exact(3) {
        println!("  {:?}", chunk);
    }
    println!("Remainder: {:?}", remainder);

    let items: Vec<i32> = (1..=10).collect();
    for (i, batch) in items.chunks(4).enumerate() {
        let sum: i32 = batch.iter().sum();
        println!("Batch {}: {:?} (sum={})", i, batch, sum);
    }

    // Matrix rows: flat array as 3-column matrix
    let matrix: Vec<i32> = (1..=9).collect();
    println!("Matrix (3 cols):");
    for row in matrix.chunks(3) {
        println!("  {:?}", row);
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_chunks_basic() {
        let data = [1i32, 2, 3, 4, 5];
        let chunks: Vec<&[i32]> = data.chunks(2).collect();
        assert_eq!(chunks.len(), 3);
        assert_eq!(chunks[0], &[1, 2]);
        assert_eq!(chunks[2], &[5]);
    }

    #[test]
    fn test_chunks_exact_remainder() {
        let data = [1i32, 2, 3, 4, 5];
        let exact = data.chunks_exact(2);
        assert_eq!(exact.remainder(), &[5]);
    }

    #[test]
    fn test_chunks_divisible() {
        let data = [1i32, 2, 3, 4];
        let chunks: Vec<_> = data.chunks(2).collect();
        assert_eq!(chunks.len(), 2);
        assert!(chunks.iter().all(|c| c.len() == 2));
    }
}
(* 263. Fixed-size chunks iteration - OCaml *)

let chunks n lst =
  let rec aux acc current count = function
    | [] ->
      if current = [] then List.rev acc
      else List.rev (List.rev current :: acc)
    | x :: xs ->
      if count = n then aux (List.rev current :: acc) [x] 1 xs
      else aux acc (x :: current) (count + 1) xs
  in
  aux [] [] 0 lst

let () =
  let nums = [1; 2; 3; 4; 5; 6; 7] in
  let cs = chunks 3 nums in
  Printf.printf "Chunks of 3:\n";
  List.iter (fun chunk ->
    Printf.printf "[%s]\n" (String.concat "; " (List.map string_of_int chunk))
  ) cs;

  let sums = List.map (List.fold_left (+) 0) cs in
  Printf.printf "Chunk sums: %s\n"
    (String.concat ", " (List.map string_of_int sums));

  let batches = chunks 4 (List.init 10 (fun i -> i + 1)) in
  List.iteri (fun i batch ->
    Printf.printf "Batch %d: [%s]\n" i
      (String.concat ", " (List.map string_of_int batch))
  ) batches