Why does `Option` support `IntoIterator`?

What use case is there for iterating over an Option?

My favorite reason, in a word, is flatten:

fn main() {
    let results = [Some(1), None, Some(3), None];
    let sum: i32 = results.into_iter().flatten().sum();
    println!("{}", sum)
}

Before Rust 1.29, you can use flat_map:

fn main() {
    let results = vec![Some(1), None, Some(3), None];
    let sum: i32 = results.into_iter().flat_map(|x| x).sum();
    println!("{}", sum)
}

Option can be thought of as a container that can hold exactly zero or one elements. Compare this to a Vec, which can hold zero or many elements. In a large set of ways, an Option is a container just like a Vec!

Implementing IntoIterator allows Option to participate in a larger share of APIs.

Note that IntoIterator is also implemented for Result, for similar reasons.

This is incredibly confusing

Yes, it is, which is why Clippy has a lint for it:

warning: for loop over `str_vec.get(0..3)`, which is an `Option`. This is more readably written as an `if let` statement
  --> src/main.rs:10:14
   |
10 |     for s in str_vec.get(0..3) {
   |              ^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(clippy::for_loops_over_fallibles)]` on by default
   = help: consider replacing `for s in str_vec.get(0..3)` with `if let Some(s) = str_vec.get(0..3)`
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles

This shows that there are ways that an Option is not like a container to a programmer.

Leave a Comment