← Back to Rust Course | Chapter 14: Closures, Iterators & Async | Lesson 2 of 6

The Iterator Trait

The Iterator trait is the recipe every list-walker in Rust follows: it just needs to know how to hand back the next item.
Syntax
rust
impl Iterator for Name {
    type Item = Type;

    fn next(&mut self) -> Option<Self::Item> {
        // body
    }
}

Calling next Manually

Every iterator provides a next() method returning Some(item) while there are more elements, and None once exhausted. A for loop is just repeatedly calling next() under the hood.

Example: Calling next Manually

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare a mutable variable `iter`, initialized to `vec![1, 2, 3].into_iter()`
    let mut iter = vec![1, 2, 3].into_iter();
    // Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
    println!("{:?}", iter.next());
    // Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
    println!("{:?}", iter.next());
    // Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
    println!("{:?}", iter.next());
    // Print "{:?}" to the console, substituting in `iter.next()` (with a trailing newline)
    println!("{:?}", iter.next());
}

Implementing a Custom Iterator

Implementing Iterator for your own type only requires defining next(); the associated Item type declares what kind of value the iterator produces.

Example: Implementing a Custom Iterator

markup
// Define a struct `Countdown` to group related fields together
struct Countdown(u32);

// Implement the `Iterator` trait for `Countdown`
impl Iterator for Countdown {
    type Item = u32;
    // Define the function `next` taking `self` and returning a `Option<u32>`
    fn next(&mut self) -> Option<u32> {
        // Check whether `self.0 == 0`
        if self.0 == 0 {
            None
        // Otherwise, run this branch
        } else {
            self.0 -= 1;
            Some(self.0 + 1)
        }
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `countdown`, set to `Countdown(3)`
    let countdown = Countdown(3);
    // Loop over `countdown`, binding each item to `n`
    for n in countdown {
        // Print "{}" to the console, substituting in `n` (with a trailing newline)
        println!("{}", n);
    }
}

Free Adapter Methods from Iterator

Once a type implements Iterator by defining next(), it automatically gains access to many built-in adapter methods, such as .sum(), without any extra work.

Example: Free Adapter Methods from Iterator

markup
// Define a struct `Countdown` to group related fields together
struct Countdown(u32);

// Implement the `Iterator` trait for `Countdown`
impl Iterator for Countdown {
    type Item = u32;
    // Define the function `next` taking `self` and returning a `Option<u32>`
    fn next(&mut self) -> Option<u32> {
        // Check whether `self.0 == 0`
        if self.0 == 0 {
            None
        // Otherwise, run this branch
        } else {
            self.0 -= 1;
            Some(self.0 + 1)
        }
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `total` (an unsigned integer), set to `Countdown(4).sum()`
    let total: u32 = Countdown(4).sum();
    // Print "Sum: {}" to the console, substituting in `total` (with a trailing newline)
    println!("Sum: {}", total);
}

Iterators Are Lazy

Iterator adapters like .map() do not run their closure immediately -- they build a pipeline description that only executes once something consumes the iterator, such as .collect() or a for loop.

Example: Iterators Are Lazy

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `numbers`, set to `vec![1, 2, 3]`
    let numbers = vec![1, 2, 3];
    let lazy_iter = numbers.iter().map(|n| {
        // Print "Processing {}" to the console, substituting in `n` (with a trailing newline)
        println!("Processing {}", n);
        n * 2
    });
    // Declare an immutable variable `result` (a `Vec<i32>`), set to `lazy_iter.collect()`
    let result: Vec<i32> = lazy_iter.collect();
    // Print "{:?}" to the console, substituting in `result` (with a trailing newline)
    println!("{:?}", result);
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting .next() returns an Option<Item>, using None to signal the iterator is exhausted.
  2. Assuming iterators are eager -- most iterator adapters like .map() are lazy and don't run until consumed.
  3. Implementing Iterator but forgetting it only requires defining next(); every other method has a default implementation built on top of it.
Chapter Summary
  • The Iterator trait requires just one method, next(&mut self) -> Option<Self::Item>.
  • Calling .next() repeatedly yields Some(item) until the sequence is exhausted, then returns None.
  • Implementing Iterator for a custom type automatically unlocks dozens of adapter methods like .map() and .filter().
  • Iterators are lazy: adapters build up a pipeline that only actually runs when consumed, such as by .collect() or a for loop.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.