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.