13.4 Advanced Iterator Traits

Beyond the base Iterator trait, several others provide additional capabilities and enable optimizations:

  • DoubleEndedIterator: For iterators that can efficiently yield elements from both the front (next()) and the back (next_back()). Enables methods like rev(). Implemented by iterators over slices, VecDeque, ranges, etc.
    fn main() {
        let numbers = vec![1, 2, 3, 4, 5];
        let mut iter = numbers.iter(); // slice::Iter implements DoubleEndedIterator
    
        assert_eq!(iter.next(), Some(&1));      // Consume from front
        assert_eq!(iter.next_back(), Some(&5)); // Consume from back
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next_back(), Some(&4));
        // Remaining elements are [&3].
        let remaining: Vec<&i32> = iter.collect();
        assert_eq!(remaining, vec![&3]);
    
        // Use rev() on a double-ended iterator
        let reversed: Vec<&i32> = numbers.iter().rev().collect();
        assert_eq!(reversed, vec![&5, &4, &3, &2, &1]);
    }
  • ExactSizeIterator: For iterators that know precisely how many elements remain. Provides len() method returning the exact count. Allows consumers like collect() to potentially pre-allocate capacity, improving performance. Implemented by iterators over slices, arrays, Vec, VecDeque, simple ranges, etc. Note: Adapters like filter or flat_map typically produce iterators that are not ExactSizeIterator, as the final count isn’t known without iterating through them.
    fn main() {
        let numbers = vec![10, 20, 30, 40];
        let mut iter = numbers.iter(); // slice::Iter implements ExactSizeIterator
        assert_eq!(iter.len(), 4);
        iter.next();
        assert_eq!(iter.len(), 3);
    
        // A filtered iterator does not know its exact size in advance
        let filtered_iter = numbers.iter().filter(|&&x| x > 15);
        // The following line would cause a compile error:
        // assert_eq!(filtered_iter.len(), 3); // Error: no method named `len` found
    
        // However, ALL iterators provide `size_hint()`
        // size_hint() returns (lower_bound, Option<upper_bound>)
        assert_eq!(filtered_iter.size_hint(), (0, Some(4))); // May be 0 to 4 elements
    
        let collected: Vec<_> = filtered_iter.collect(); // Iteration happens here
        assert_eq!(collected.len(), 3); // Actual count after iteration
    }
  • size_hint(): A method available on all iterators via the Iterator trait. Returns a tuple (lower_bound, Option<upper_bound>) estimating the number of remaining elements. The lower bound is guaranteed to be accurate. For ExactSizeIterator, lower_bound == upper_bound.unwrap(), and len() is simply a convenience method for this. size_hint is used internally by methods like collect to make initial capacity reservations.
  • FusedIterator: A marker trait indicating that once the iterator returns None, all subsequent calls to next() (and next_back() if applicable) are guaranteed to return None. Most standard iterators are fused. This allows consumers to potentially optimize by not needing to call next() again after the first None. Custom iterators should uphold this behavior if possible and can implement this marker trait.