Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

foreach - Why does `Option` support `IntoIterator`?

I was trying to iterate over a subsection of a vector of strings, i.e. a subslice of Vec<String>. Within each iteration, I wanted to pass the string as a slice to a function.

I didn't notice that Vec::get returns an Option, and thought I could just directly iterate over the return value:

fn take_str(s: &str) {
    println!("{}", s);
}

fn main() {
    let str_vec: Vec<String> =
        ["one", "two", "three", "uno", "dos", "tres"].iter().map(|&s| 
        s.into()).collect();
    for s in str_vec.get(0..3) {
        take_str(&s); // Type mismatch: found type `&&[std::string::String]`
    }
}

Clearly, I was expecting s to be a String, but it's actually &[String]. This is because my for loop is actually iterating over the Option returned by Vec::get().

I also wrote the following code, which demonstrates that the for loop is in fact unwrapping an Option:

let foo = Option::Some ( ["foo".to_string()] );
for f in foo {
    take_str(&f); // Same error as above, showing `f` is of type `&[String]`
}

But this is incredibly confusing; I never expected (until I wrote this code and figured out what it's actually doing) that Option could be unwrapped by iterating over it. Why is that supported? What use case is there for iterating over an Option?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

What use case is there for iterating over an Option?

My favorite reason, in a word, is flatten:

fn main() {
    let results = vec![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.

But 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.

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...