ExamplesBy LevelBy TopicLearning Paths
279 Intermediate

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

  • • Understand that nth(n) advances the iterator past elements 0..n and returns element n
  • • Recognize that calling nth(n) mutates the iterator — subsequent calls continue from n+1
  • • Use consecutive nth() calls to step through an iterator at varying intervals
  • • Distinguish from indexing: nth() is O(n) for generic iterators, O(1) for indexed collections
  • Code 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

  • Safety: Rust returns Option<T> and never panics; OCaml's List.nth raises on invalid index, requiring nth_opt or exception handling.
  • Stateful advancement: Rust's 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.
  • O(n) for lists: Both languages traverse up to n elements to access position n on linked structures; arrays/slices do O(1) indexing.
  • Iterator position: After 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

  • Use nth() to implement a take_every_nth function that collects every nth element of an iterator without using step_by.
  • Write a function that takes an iterator and an index i, returning the i-th element but consuming the iterator up to that point — show that the iterator is advanced.
  • Implement nth_from_back for a DoubleEndedIterator using next_back() and a loop.
  • Open Source Repos