ExamplesBy LevelBy TopicLearning Paths
094 Intermediate

094 — Peekable Iterator

Functional Programming

Tutorial

The Problem

Use .peekable() to look ahead at the next iterator element without consuming it. Implement dedup — removing consecutive duplicates — as a demonstration: consume the current value, then skip ahead while the peek matches. Compare with OCaml's manual peekable wrapper built on Seq.

🎯 Learning Outcomes

  • • Convert any iterator to a Peekable<I> with .peekable()
  • • Use iter.peek() to inspect the next item as Option<&Item> without advancing
  • • Combine next() and peek() in a while let loop for look-ahead parsing
  • • Understand the double-reference in peek()Some(&&val) for &[i32]
  • • Map Rust's built-in Peekable to OCaml's manually implemented peekable wrapper
  • • Recognise peekable iterators as essential for tokenizers and parsers
  • Code Example

    #![allow(clippy::all)]
    // 094: Peekable Iterator
    
    fn dedup(v: &[i32]) -> Vec<i32> {
        let mut result = Vec::new();
        let mut iter = v.iter().peekable();
        while let Some(&val) = iter.next() {
            result.push(val);
            while iter.peek() == Some(&&val) {
                iter.next();
            }
        }
        result
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_dedup() {
            assert_eq!(dedup(&[1, 1, 2, 2, 2, 3, 3, 1]), vec![1, 2, 3, 1]);
            assert_eq!(dedup(&[]), Vec::<i32>::new());
            assert_eq!(dedup(&[5]), vec![5]);
        }
    
        #[test]
        fn test_peekable() {
            let mut iter = [1, 2, 3].iter().peekable();
            assert_eq!(iter.peek(), Some(&&1));
            assert_eq!(iter.next(), Some(&1));
            assert_eq!(iter.peek(), Some(&&2));
        }
    }

    Key Differences

    AspectRustOCaml
    Built-inIterator::peekable()Manual peekable struct
    Peek typeOption<&Item> (reference)'a option (value)
    Double refSome(&&val) for &[T] iteratorsSome x directly
    MutableInternally mutable in adapterMutable fields in record
    Parser useCommon in tokenisersSame manual wrapper
    StandardYesNo

    Peekable iterators are the foundation of hand-written parsers and tokenisers. By looking ahead without consuming, you can make branching decisions based on context — the backbone of recursive descent parsing.

    OCaml Approach

    OCaml's Seq has no built-in peek. The peekable record holds peeked: 'a option and seq: 'a Seq.t ref. peek checks the peeked slot, or forces the next thunk and caches it. next drains the peeked slot or forces the seq. This is a complete, correct implementation — just more code than Rust's built-in.

    Full Source

    #![allow(clippy::all)]
    // 094: Peekable Iterator
    
    fn dedup(v: &[i32]) -> Vec<i32> {
        let mut result = Vec::new();
        let mut iter = v.iter().peekable();
        while let Some(&val) = iter.next() {
            result.push(val);
            while iter.peek() == Some(&&val) {
                iter.next();
            }
        }
        result
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_dedup() {
            assert_eq!(dedup(&[1, 1, 2, 2, 2, 3, 3, 1]), vec![1, 2, 3, 1]);
            assert_eq!(dedup(&[]), Vec::<i32>::new());
            assert_eq!(dedup(&[5]), vec![5]);
        }
    
        #[test]
        fn test_peekable() {
            let mut iter = [1, 2, 3].iter().peekable();
            assert_eq!(iter.peek(), Some(&&1));
            assert_eq!(iter.next(), Some(&1));
            assert_eq!(iter.peek(), Some(&&2));
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_dedup() {
            assert_eq!(dedup(&[1, 1, 2, 2, 2, 3, 3, 1]), vec![1, 2, 3, 1]);
            assert_eq!(dedup(&[]), Vec::<i32>::new());
            assert_eq!(dedup(&[5]), vec![5]);
        }
    
        #[test]
        fn test_peekable() {
            let mut iter = [1, 2, 3].iter().peekable();
            assert_eq!(iter.peek(), Some(&&1));
            assert_eq!(iter.next(), Some(&1));
            assert_eq!(iter.peek(), Some(&&2));
        }
    }

    Deep Comparison

    Core Insight

    Peekable lets you inspect the next element without consuming it — essential for parsers and tokenizers

    OCaml Approach

  • • See example.ml for implementation
  • Rust Approach

  • • See example.rs for implementation
  • Comparison Table

    FeatureOCamlRust
    Seeexample.mlexample.rs

    Exercises

  • Implement a simple tokeniser that groups consecutive digit characters into Token::Number(u64) and other characters into Token::Other(char) using a peekable iterator.
  • Write group_by_peekable<T: PartialEq>(v: &[T]) -> Vec<Vec<T>> that groups consecutive equal elements using peekable.
  • Implement parse_csv_field(iter: &mut Peekable<impl Iterator<Item=char>>) -> String that reads until a comma or end of input.
  • Use peek to implement a take_while_peek that advances as long as the peeked element satisfies a predicate, stopping before consuming the first non-matching element.
  • In OCaml, add a peek_nth function to the peekable wrapper that allows looking n elements ahead by caching a VecDeque-style buffer.
  • Open Source Repos