275: Finding Extremes with min() and max()
Functional Programming
Tutorial
The Problem
Finding the minimum or maximum of a collection is a fundamental algorithm — used in sorting, priority selection, range computation, and statistical analysis. The challenge is handling the empty-collection case safely. Rust's min() and max() return Option<T>, forcing callers to handle the empty case explicitly, while the min_by_key() variants enable comparison by a derived property without needing to implement Ord on the whole type.
🎯 Learning Outcomes
min() and max() return Option<T> — None for empty iteratorsmin_by_key() and max_by_key() to find extremes by a derived attributeOrd and compare lexicographically for compound typesmin_by() and max_by() for custom comparison functions (e.g., floating-point)Code Example
#![allow(clippy::all)]
//! 275. Finding extremes: min() and max()
//!
//! `min()` and `max()` return `Option<T>` — None for empty, requires `Ord`.
#[cfg(test)]
mod tests {
#[test]
fn test_min_max() {
let v = [5i32, 3, 8, 1, 9, 2];
assert_eq!(v.iter().min(), Some(&1));
assert_eq!(v.iter().max(), Some(&9));
}
#[test]
fn test_min_max_empty() {
let empty: Vec<i32> = vec![];
assert_eq!(empty.iter().min(), None);
assert_eq!(empty.iter().max(), None);
}
#[test]
fn test_min_by_key() {
let words = ["hello", "hi", "world"];
assert_eq!(words.iter().min_by_key(|w| w.len()), Some(&"hi"));
}
#[test]
fn test_max_by_key() {
let words = ["hello", "hi", "world"];
assert_eq!(words.iter().max_by_key(|w| w.len()), Some(&"world"));
}
}Key Differences
Option<T> forcing empty-case handling; OCaml's standard min on lists panics on empty or requires manual wrapping.min()/max() require Ord; floating-point types use min_by with partial_cmp due to NaN.min_by_key (cheap key extraction) and min_by (custom comparison function) as separate methods.max() returns the last of equal elements; min() returns the first — important when elements are equal by Ord but distinct.OCaml Approach
OCaml uses List.fold_left with min (a built-in function for comparable types), or List.sort then List.hd. There is no standard min/max for lists that returns Option:
let minimum = function
| [] -> None
| xs -> Some (List.fold_left min (List.hd xs) (List.tl xs))
let min_by_key f lst =
List.fold_left (fun acc x -> if f x < f acc then x else acc) (List.hd lst) lst
Full Source
#![allow(clippy::all)]
//! 275. Finding extremes: min() and max()
//!
//! `min()` and `max()` return `Option<T>` — None for empty, requires `Ord`.
#[cfg(test)]
mod tests {
#[test]
fn test_min_max() {
let v = [5i32, 3, 8, 1, 9, 2];
assert_eq!(v.iter().min(), Some(&1));
assert_eq!(v.iter().max(), Some(&9));
}
#[test]
fn test_min_max_empty() {
let empty: Vec<i32> = vec![];
assert_eq!(empty.iter().min(), None);
assert_eq!(empty.iter().max(), None);
}
#[test]
fn test_min_by_key() {
let words = ["hello", "hi", "world"];
assert_eq!(words.iter().min_by_key(|w| w.len()), Some(&"hi"));
}
#[test]
fn test_max_by_key() {
let words = ["hello", "hi", "world"];
assert_eq!(words.iter().max_by_key(|w| w.len()), Some(&"world"));
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_min_max() {
let v = [5i32, 3, 8, 1, 9, 2];
assert_eq!(v.iter().min(), Some(&1));
assert_eq!(v.iter().max(), Some(&9));
}
#[test]
fn test_min_max_empty() {
let empty: Vec<i32> = vec![];
assert_eq!(empty.iter().min(), None);
assert_eq!(empty.iter().max(), None);
}
#[test]
fn test_min_by_key() {
let words = ["hello", "hi", "world"];
assert_eq!(words.iter().min_by_key(|w| w.len()), Some(&"hi"));
}
#[test]
fn test_max_by_key() {
let words = ["hello", "hi", "world"];
assert_eq!(words.iter().max_by_key(|w| w.len()), Some(&"world"));
}
}
Exercises
Vec<(String, i32)> using max_by_key().fold() to compute a (min, max) tuple.median function that uses min() and max() on sub-iterators — or uses sorting and indexing — and handles empty input.