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

275: Iterator min() and max()

Difficulty: 1 Level: Beginner Consume an iterator to find its minimum or maximum element, returning `Option<T>` to handle the empty case.

The Problem This Solves

You want the smallest or largest value in a collection. Without `min()` and `max()`, you'd write a fold with a running minimum, initialize it to `i32::MAX`, and hope you remembered to handle the empty case. Or you'd sort and take the first element โ€” O(n log n) when O(n) is all you need. The more interesting problem is finding the element with the minimum of a derived property โ€” the shortest string, the student with the highest score, the point closest to the origin. `.min_by_key(|x| x.score)` handles this without implementing `Ord` on your struct or creating intermediate (value, key) tuples. One important constraint: `min()` and `max()` require `Ord`. That means `f64` doesn't work directly โ€” floats don't have a total ordering because of `NaN`. The example shows the `reduce(f64::min)` workaround for floats.

The Intuition

Walk the entire iterator, keep track of the extreme value seen so far, return it wrapped in `Some` โ€” or `None` if the iterator was empty.

How It Works in Rust

let nums = [3i32, 1, 4, 1, 5, 9, 2, 6];
println!("{:?}", nums.iter().min());  // Some(1)
println!("{:?}", nums.iter().max());  // Some(9)

// Empty iterator โ†’ None (not a crash, not a sentinel value)
let empty: Vec<i32> = vec![];
println!("{:?}", empty.iter().min());  // None

// min/max by a derived key โ€” no need to implement Ord
let words = ["banana", "apple", "fig", "cherry"];
words.iter().min_by_key(|w| w.len());  // Some("fig")
words.iter().max_by_key(|w| w.len());  // Some("banana")

// Works on any type with a comparable field
struct Student { name: &'static str, score: u32 }
let students = [/* ... */];
students.iter().max_by_key(|s| s.score);  // top scorer

// f64 doesn't implement Ord โ€” use reduce instead
let floats = [3.14f64, 1.41, 2.71];
let min_f = floats.iter().cloned().reduce(f64::min);  // Some(1.41)

What This Unlocks

Key Differences

ConceptOCamlRust
Min of list`List.fold_left min max_int lst``iter.min()`
Empty listManual guard or exceptionReturns `None` automatically
Float minWorks (but NaN is UB)Must use `reduce(f64::min)` or `min_by`
By keyManual: `List.sort` + `List.hd``.min_by_key(f)` / `.max_by_key(f)`
Return type`'a` (unwrapped)`Option<T>`
//! 275. Finding extremes: min() and max()
//!
//! `min()` and `max()` consume an iterator and return `Option<T>`.
//! They require `Ord` โ€” use `min_by_key` / `max_by_key` for structs,
//! and `reduce(f64::min)` for floats which lack a total ordering.

/// Find the minimum integer in a slice, returning None for empty input.
pub fn slice_min(nums: &[i32]) -> Option<i32> {
    nums.iter().copied().min()
}

/// Find the maximum integer in a slice, returning None for empty input.
pub fn slice_max(nums: &[i32]) -> Option<i32> {
    nums.iter().copied().max()
}

/// Return the shortest string in a slice by character count.
pub fn shortest<'a>(words: &[&'a str]) -> Option<&'a str> {
    words.iter().copied().min_by_key(|w| w.len())
}

/// Return the longest string in a slice by character count.
pub fn longest<'a>(words: &[&'a str]) -> Option<&'a str> {
    words.iter().copied().max_by_key(|w| w.len())
}

#[derive(Debug, PartialEq)]
pub struct Student {
    pub name: &'static str,
    pub score: u32,
}

/// Return the student with the highest score.
pub fn top_student(students: &[Student]) -> Option<&Student> {
    students.iter().max_by_key(|s| s.score)
}

/// Return the student with the lowest score.
pub fn bottom_student(students: &[Student]) -> Option<&Student> {
    students.iter().min_by_key(|s| s.score)
}

/// Find the minimum of f64 values using reduce, since f64 is not Ord.
/// Returns None for empty input or if any value is NaN.
pub fn float_min(nums: &[f64]) -> Option<f64> {
    nums.iter().copied().reduce(f64::min)
}

/// Find the maximum of f64 values using reduce, since f64 is not Ord.
pub fn float_max(nums: &[f64]) -> Option<f64> {
    nums.iter().copied().reduce(f64::max)
}

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

    #[test]
    fn test_min_max_integers() {
        let nums = [3i32, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
        assert_eq!(slice_min(&nums), Some(1));
        assert_eq!(slice_max(&nums), Some(9));
    }

    #[test]
    fn test_empty_returns_none() {
        let empty: &[i32] = &[];
        assert_eq!(slice_min(empty), None);
        assert_eq!(slice_max(empty), None);
    }

    #[test]
    fn test_single_element() {
        assert_eq!(slice_min(&[42]), Some(42));
        assert_eq!(slice_max(&[42]), Some(42));
    }

    #[test]
    fn test_min_max_by_key_strings() {
        let words = ["banana", "apple", "fig", "kiwi", "cherry"];
        assert_eq!(shortest(&words), Some("fig"));
        // max_by_key returns last of equal elements; "cherry" ties "banana" at len 6
        assert_eq!(longest(&words), Some("cherry"));
    }

    #[test]
    fn test_min_max_by_key_struct() {
        let students = vec![
            Student { name: "Alice", score: 95 },
            Student { name: "Bob", score: 72 },
            Student { name: "Carol", score: 88 },
        ];
        assert_eq!(top_student(&students).map(|s| s.name), Some("Alice"));
        assert_eq!(bottom_student(&students).map(|s| s.name), Some("Bob"));
    }

    #[test]
    fn test_float_min_max() {
        let floats = [3.1f64, 1.4, 1.5, 9.2, 2.6];
        assert_eq!(float_min(&floats), Some(1.4));
        assert_eq!(float_max(&floats), Some(9.2));
    }

    #[test]
    fn test_float_empty() {
        assert_eq!(float_min(&[]), None);
        assert_eq!(float_max(&[]), None);
    }

    #[test]
    fn test_all_same_values() {
        let nums = [7i32, 7, 7, 7];
        assert_eq!(slice_min(&nums), Some(7));
        assert_eq!(slice_max(&nums), Some(7));
    }
}
(* 275. Finding extremes: min() and max() - OCaml *)

let list_min = function [] -> None | lst -> Some (List.fold_left min max_int lst)
let list_max = function [] -> None | lst -> Some (List.fold_left max min_int lst)

let () =
  let nums = [3; 1; 4; 1; 5; 9; 2; 6; 5; 3; 5] in
  Printf.printf "Min: %s\n" (match list_min nums with Some n -> string_of_int n | None -> "None");
  Printf.printf "Max: %s\n" (match list_max nums with Some n -> string_of_int n | None -> "None");
  Printf.printf "Min of []: %s\n" (match list_min [] with None -> "None" | Some n -> string_of_int n);

  let words = ["banana"; "apple"; "fig"; "kiwi"; "cherry"] in
  let shortest = List.fold_left (fun acc w ->
    if String.length w < String.length acc then w else acc
  ) (List.hd words) (List.tl words) in
  Printf.printf "Shortest: %s\n" shortest;

  let longest = List.fold_left (fun acc w ->
    if String.length w > String.length acc then w else acc
  ) (List.hd words) (List.tl words) in
  Printf.printf "Longest: %s\n" longest