917-iterator-last — Iterator Last
Tutorial
The Problem
Retrieving the final element of a sequence has different costs depending on the data structure: O(1) for arrays and Vec (by index), O(n) for linked lists (must traverse all). For iterators, .last() always traverses the entire sequence — it must consume every element to reach the end. This makes .last() unsuitable for large sequences where only the final element is needed; a DoubleEndedIterator with .next_back() is O(1) for slices. Understanding this cost difference helps write efficient code. OCaml has List.rev xs |> List.hd which is similarly O(n).
🎯 Learning Outcomes
.last() to retrieve the final element of an iterator.last() consumes the entire iterator — O(n) alwaysDoubleEndedIterator::next_back() as O(1) alternative for slices.last() after filtering or mapping to get the last matching elementList.rev |> List.hd and array back-indexingCode Example
#![allow(clippy::all)]
//! 278. Getting the last element
//!
//! `last()` consumes the iterator to return `Option<T>` with the final element.
#[cfg(test)]
mod tests {
#[test]
fn test_last_basic() {
let v = [1i32, 2, 3, 4, 5];
assert_eq!(v.iter().last(), Some(&5));
}
#[test]
fn test_last_empty() {
let empty: Vec<i32> = vec![];
assert_eq!(empty.iter().last(), None);
}
#[test]
fn test_last_after_filter() {
let last_even = (1i32..=10).filter(|x| x % 2 == 0).last();
assert_eq!(last_even, Some(10));
}
#[test]
fn test_last_single() {
assert_eq!([42i32].iter().last(), Some(&42));
}
}Key Differences
.last() returns Option<T> — None for empty; OCaml List.hd raises on empty list (List.last_opt from Base is safe)..last() on iterators and OCaml List.last are O(n); array/slice back-access is O(1) in both languages..last() on all iterators; OCaml standard library lacks List.last (use Base or recursion).DoubleEndedIterator::next_back() provides O(1) last-element access for slices and many other iterators.OCaml Approach
List.rev xs |> List.hd is the standard but inefficient approach — O(n) reversal then O(1) head. let rec last = function | [] -> None | [x] -> Some x | _ :: rest -> last rest is O(n) with O(1) allocation. Array.get arr (Array.length arr - 1) is O(1) for arrays. OCaml lacks a standard List.last in the standard library (it is in Base.List.last).
Full Source
#![allow(clippy::all)]
//! 278. Getting the last element
//!
//! `last()` consumes the iterator to return `Option<T>` with the final element.
#[cfg(test)]
mod tests {
#[test]
fn test_last_basic() {
let v = [1i32, 2, 3, 4, 5];
assert_eq!(v.iter().last(), Some(&5));
}
#[test]
fn test_last_empty() {
let empty: Vec<i32> = vec![];
assert_eq!(empty.iter().last(), None);
}
#[test]
fn test_last_after_filter() {
let last_even = (1i32..=10).filter(|x| x % 2 == 0).last();
assert_eq!(last_even, Some(10));
}
#[test]
fn test_last_single() {
assert_eq!([42i32].iter().last(), Some(&42));
}
}#[cfg(test)]
mod tests {
#[test]
fn test_last_basic() {
let v = [1i32, 2, 3, 4, 5];
assert_eq!(v.iter().last(), Some(&5));
}
#[test]
fn test_last_empty() {
let empty: Vec<i32> = vec![];
assert_eq!(empty.iter().last(), None);
}
#[test]
fn test_last_after_filter() {
let last_even = (1i32..=10).filter(|x| x % 2 == 0).last();
assert_eq!(last_even, Some(10));
}
#[test]
fn test_last_single() {
assert_eq!([42i32].iter().last(), Some(&42));
}
}
Exercises
second_to_last<T>(data: &[T]) -> Option<&T> without reversing the slice.last_n<T: Clone>(iter: impl Iterator<Item=T>, n: usize) -> Vec<T> that collects the last n elements without knowing the total count in advance.last_matching<T>(data: &[T], pred: impl Fn(&T) -> bool) -> Option<&T> using a reverse iterator for O(1) discovery when the match is near the end.