096 — Exact Size Iterator
Tutorial
The Problem
Demonstrate Rust's ExactSizeIterator trait — iterators that know their length in O(1). Show that Vec::iter(), ranges, and enumerate all implement ExactSizeIterator, enabling .len() without traversal. Demonstrate chunks_exact for obtaining only full-sized chunks plus a remainder. Compare with OCaml's O(1) Array.length versus O(n) List.length.
🎯 Learning Outcomes
.len() on any ExactSizeIterator in O(1).len() on iterators is distinct from .count() (which consumes)chunks_exact(n) to iterate only complete chunks and access the remainderExactSizeIterator (knows length) from Iterator (does not)Array.length O(1)Vec::with_capacityCode Example
#![allow(clippy::all)]
// 096: Exact Size Iterator
#[cfg(test)]
mod tests {
#[test]
fn test_exact_size() {
let v = vec![1, 2, 3, 4, 5];
assert_eq!(v.iter().len(), 5);
assert_eq!((0..10).len(), 10);
}
#[test]
fn test_enumerate_len() {
let v = vec!["a", "b", "c"];
let e: Vec<_> = v.iter().enumerate().collect();
assert_eq!(e, vec![(0, &"a"), (1, &"b"), (2, &"c")]);
}
#[test]
fn test_chunks_exact() {
let v = vec![1, 2, 3, 4, 5];
let c: Vec<&[i32]> = v.chunks_exact(2).collect();
assert_eq!(c, vec![&[1, 2][..], &[3, 4][..]]);
assert_eq!(v.chunks_exact(2).remainder(), &[5]);
}
}Key Differences
| Aspect | Rust | OCaml |
|---|---|---|
| O(1) length | ExactSizeIterator::len() | Array.length |
| O(n) length | Iterator::count() (consumes!) | List.length |
| Chunks with remainder | chunks_exact(n).remainder() | Manual split |
| Enumerate | .enumerate() (preserves ExactSize) | List.mapi |
| Protocol | Trait ExactSizeIterator | No equivalent |
| Consuming length | .count() drains iterator | Seq.length or List.length |
ExactSizeIterator::len() is non-consuming — unlike .count(), it does not advance the iterator. This is critical when you want to pre-allocate (Vec::with_capacity(iter.len())) without losing the ability to iterate.
OCaml Approach
OCaml arrays have O(1) Array.length; lists require O(n) List.length. There is no ExactSizeIterator protocol — arrays inherently carry their length, and sequential iteration with for i = 0 to Array.length a - 1 do naturally provides sized bounds. List.mapi provides enumerate-style indexed iteration.
Full Source
#![allow(clippy::all)]
// 096: Exact Size Iterator
#[cfg(test)]
mod tests {
#[test]
fn test_exact_size() {
let v = vec![1, 2, 3, 4, 5];
assert_eq!(v.iter().len(), 5);
assert_eq!((0..10).len(), 10);
}
#[test]
fn test_enumerate_len() {
let v = vec!["a", "b", "c"];
let e: Vec<_> = v.iter().enumerate().collect();
assert_eq!(e, vec![(0, &"a"), (1, &"b"), (2, &"c")]);
}
#[test]
fn test_chunks_exact() {
let v = vec![1, 2, 3, 4, 5];
let c: Vec<&[i32]> = v.chunks_exact(2).collect();
assert_eq!(c, vec![&[1, 2][..], &[3, 4][..]]);
assert_eq!(v.chunks_exact(2).remainder(), &[5]);
}
}#[cfg(test)]
mod tests {
#[test]
fn test_exact_size() {
let v = vec![1, 2, 3, 4, 5];
assert_eq!(v.iter().len(), 5);
assert_eq!((0..10).len(), 10);
}
#[test]
fn test_enumerate_len() {
let v = vec!["a", "b", "c"];
let e: Vec<_> = v.iter().enumerate().collect();
assert_eq!(e, vec![(0, &"a"), (1, &"b"), (2, &"c")]);
}
#[test]
fn test_chunks_exact() {
let v = vec![1, 2, 3, 4, 5];
let c: Vec<&[i32]> = v.chunks_exact(2).collect();
assert_eq!(c, vec![&[1, 2][..], &[3, 4][..]]);
assert_eq!(v.chunks_exact(2).remainder(), &[5]);
}
}
Deep Comparison
Core Insight
ExactSizeIterator knows its length upfront — enables optimized allocation and bounds checking
OCaml Approach
Rust Approach
Comparison Table
| Feature | OCaml | Rust |
|---|---|---|
| See | example.ml | example.rs |
Exercises
collect_with_capacity<I: ExactSizeIterator<Item=T>>(iter: I) -> Vec<T> that pre-allocates exactly the right capacity.ExactSizeIterator for a custom Counter struct that counts from start to end..map(f) on an ExactSizeIterator preserves ExactSizeIterator by checking that the mapped iterator also has .len().chunks_exact(3).remainder() to process a byte buffer in 3-byte UTF-8 sequences, handling the partial final chunk separately.sized_seq type that pairs a Seq.t with a known int length and implement map that preserves the size.