279: Random Access with nth()
Functional Programming
Tutorial
The Problem
Accessing the element at a specific zero-based index in an iterator requires advancing past all preceding elements. Unlike array indexing (O(1)), nth(n) on a generic iterator is O(n) — consuming and discarding the first n elements. Crucially, calling nth(n) advances the iterator state, so subsequent calls continue from where the previous one left off. This makes nth() useful for interleaved stepping through a stream.
🎯 Learning Outcomes
nth(n) advances the iterator past elements 0..n and returns element nnth(n) mutates the iterator — subsequent calls continue from n+1nth() calls to step through an iterator at varying intervalsnth() is O(n) for generic iterators, O(1) for indexed collectionsCode Example
#![allow(clippy::all)]
//! 279. Random access with nth()
//!
//! `nth(n)` returns `Option<T>` at index n, consuming elements 0..n in the process.
#[cfg(test)]
mod tests {
#[test]
fn test_nth_basic() {
let v = [10i32, 20, 30, 40];
assert_eq!(v.iter().nth(2), Some(&30));
}
#[test]
fn test_nth_out_of_bounds() {
let v = [1i32, 2];
assert_eq!(v.iter().nth(5), None);
}
#[test]
fn test_nth_advances_iterator() {
let mut it = [1i32, 2, 3, 4, 5].iter();
assert_eq!(it.nth(1), Some(&2)); // consumes 1,2
assert_eq!(it.nth(0), Some(&3)); // now at 3
}
#[test]
fn test_nth_zero() {
let v = [99i32];
assert_eq!(v.iter().nth(0), Some(&99));
}
}Key Differences
Option<T> and never panics; OCaml's List.nth raises on invalid index, requiring nth_opt or exception handling.nth(n) advances the iterator state — calling it repeatedly steps through positions; OCaml's List.nth on the original list does not have this property.nth(n), Rust's iterator is positioned at n+1 — the next next() or nth(0) returns element n+1.OCaml Approach
OCaml uses List.nth lst n for direct indexed access (zero-based, raises Invalid_argument on out-of-bounds). For safe access with Option, wrap in a try-catch or use List.nth_opt (OCaml 4.05+):
let nth_opt lst n =
try Some (List.nth lst n) with Invalid_argument _ -> None
(* Or tail-recursive: *)
let rec nth_opt lst n = match lst with
| [] -> None
| x :: _ when n = 0 -> Some x
| _ :: xs -> nth_opt xs (n-1)
Full Source
#![allow(clippy::all)]
//! 279. Random access with nth()
//!
//! `nth(n)` returns `Option<T>` at index n, consuming elements 0..n in the process.
#[cfg(test)]
mod tests {
#[test]
fn test_nth_basic() {
let v = [10i32, 20, 30, 40];
assert_eq!(v.iter().nth(2), Some(&30));
}
#[test]
fn test_nth_out_of_bounds() {
let v = [1i32, 2];
assert_eq!(v.iter().nth(5), None);
}
#[test]
fn test_nth_advances_iterator() {
let mut it = [1i32, 2, 3, 4, 5].iter();
assert_eq!(it.nth(1), Some(&2)); // consumes 1,2
assert_eq!(it.nth(0), Some(&3)); // now at 3
}
#[test]
fn test_nth_zero() {
let v = [99i32];
assert_eq!(v.iter().nth(0), Some(&99));
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_nth_basic() {
let v = [10i32, 20, 30, 40];
assert_eq!(v.iter().nth(2), Some(&30));
}
#[test]
fn test_nth_out_of_bounds() {
let v = [1i32, 2];
assert_eq!(v.iter().nth(5), None);
}
#[test]
fn test_nth_advances_iterator() {
let mut it = [1i32, 2, 3, 4, 5].iter();
assert_eq!(it.nth(1), Some(&2)); // consumes 1,2
assert_eq!(it.nth(0), Some(&3)); // now at 3
}
#[test]
fn test_nth_zero() {
let v = [99i32];
assert_eq!(v.iter().nth(0), Some(&99));
}
}
Exercises
nth() to implement a take_every_nth function that collects every nth element of an iterator without using step_by.i, returning the i-th element but consuming the iterator up to that point — show that the iterator is advanced.nth_from_back for a DoubleEndedIterator using next_back() and a loop.