Reputation: 16595
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());
}
}
Upvotes: 3
Views: 2731
Reputation: 70830
Since Rust 1.65.0, you can use a let
-else
statement, which saves you one level of indentation for the loop's body:
for opt in &vector {
let Some(num) = opt else { continue };
println!("Number: {}", num);
}
Upvotes: 1
Reputation: 601351
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.
Upvotes: 10
Reputation: 154836
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.
Upvotes: 5