So what I want to do is break out of an iterator. In this simplified test code, I want to stop printing the contents of the range when the number is 5. How would I do this? As in other languages, the break keyword is only available in loops.
It's not very convenient to use iterator approach in some cases, and sometimes it doesn't work (await in the closure for example).
A simple loop will be good.
for_each (aka the simplest fold) always† runs to the end of the iterator -- that's why it takes self, since when it's done there's nothing more useful to do with the iterator.
The version that can end early is try_for_each (aka the simplest try_fold), which looks at the result of the closure -- and takes &mut self since it can stop early so you might want to use the iterator again after it.
There are some ways around this, but using some of these clever tricks hurts the readability of the code.
"Use the right tool for the right job".
Iterator adapters aren't really the right tool if all you want to do for each item in the iterator is run some code solely for its side effects. If you're not adapting the value of each item in the iterator in some way, iterator adapters probably aren't the right tool.
for x in (1..=10) {
println!("{}", x);
if x == 5 {
break;
}
}
I might butcher this explanation... but very loosely speaking: An iterator adapter is something that takes an iterator as an input, and changes that iterator in some way. Let's use (1..=10) as our iterator for the example.
for i in 1..=10 {
println!("{}", i);
}
will just print the numbers 1 through to 10. But let's say we only wanted to print the even numbers. We could do
for i in 1..=10 {
if i % 2 == 0 {
println!("{}", i);
}
}
to do nothing for the iterations where i is odd. Or, we could filter out the iterations where i is odd:
for i in (1..=10).filter(|x| x % 2 == 0) {
println!("{}", i);
}
The .filter() call here is an iterator adapter that takes (1..=10) as an input, and returns a new iterator that skips the odd numbers. You can often use multiple iterator adapters together. This one will square the even numbers from 1 to 10. The .map() is an iterator adapter:
for i in (1..=10).filter(|x| x % 2 == 0).map(|x| x * x) {
println!("{}", i);
}
And here's one that uses the .sum() adaptor:
let sum: i32 = (1..=10).filter(|x| x % 2 == 0).map(|x| x * x).sum();
println!("{}", sum);
The sum iterator adaptor takes an iterator as an input and uses it to calculate and return a sum, but doesn't return another iterator.
Same case with for_each except that for_each doesn't return anything.
Strictly speaking, sum (like for_each, fold, collect etc.) is not an adapter, but a consumer - i.e. the method which converts an iterator into something which is not an iterator. The crucial difference is in the fact that iterators (and therefore iterator adapters) are lazy - the iteration only happens inside the consumer.
I especially feel that with the nightly-using version above. The block only conditionally returning an enum while falling through and returning () in the other case is extremely confusing, for one.
Yes, concretely it's the absence (or rather, implicit presence) of an outer Some (or is it Ok? I can't tell, that's why it bothers me), which is apparent in the stable version.
Well, sort of, but not mainly. Your latest example is still better than a plain try in that I can go to the docs or the definition of Yolo::whatever and see what it's doing, and I know it's likely implemented for Option. So at least there's a concrete trait, even if the type is inferred. try doesn't tell me any of that information, I have to know what exactly it does.