简体   繁体   中英

Rust stacked optional in while let

I am really new in rust, and while going through the rustlings exercises I found something I do not fully understand regarding stacked Options.

Given the following code:

let vector = vec![Some(24), None, Some(42)];
let mut iter = vector.iter();
while let Some(Some(number)) = iter.next() {
    println!("Number: {}", number);
}

I would expect to see the following output:

Number: 24
Number: 42

But I guess as soon as rust encounters the None , the while loop exits, only printing the 24

What would be the most idiomatic rust code to iterate and unwrap optional values? The closest that I got would look something like this:

let mut iter = vector.iter();
while let Some(iterNext) = iter.next() {
    if let Some(num) = iterNext {
        println!("Number: {}", num);
    }
}

Or it could also be done by checking the existence in a for loop:

for opt in &vector {
    if opt.is_some() {
        println!("Number: {}", opt.unwrap());
    }
}

Another nice way to write this code is

for num in vector.iter().flatten() {
    println!("{}", num);
}

The flatten() method on an iterator treats each item of the iterator as an iterator, and returns an iterator over all these iterators chained together. Option is an iterator that yields one element if it is Some , or none for None , so flatten() is exactly what we want here.

Of course you could also write this using for_each() , but for code with side effects I generally prefer for loops.

I would expect to see the following output: [...]

A while loop that encounters a false condition exits - but that's not specific to Rust, it's just how while loops work.

An idiomatic solution would probably combine your last two snippets:

for opt in &vector {
    if let Some(num) = opt {
        println!("Number: {}", num);
    }
}

Just a simple for loop containing an if let , no unwrapping required.

Another idiomatic variant is to use iterator adapters:

vector
    .iter()
    .filter_map(|opt| opt.as_ref())
    .for_each(|num| println!("{}", num));

Note that here we could use filter(Option::is_some) , but then we would be left with options and would have to use unwrap() to get to the values. This is where filter_map() comes in useful: it filters the Some values (after applying the map function), and at the same time extracts the values inside. opt.as_ref() serves to trivially convert &Option<T> , obtained from iterating a vector of options by reference, to Option<&T> which filter_map expects returned.

Using and_then to filter out the None 's and only delivering Some 's to the programs working part:

let vector = vec![Some(24), None, Some(42)];
for num in vector.iter() {
    num.and_then::<i32, fn(i32) -> Option<i32>>(|n| {
        println!("{}", n);  // …working part
        None
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM