Consider an example where one needs to iterate over a sequence with all but the output yes/no requires unwrapping somehow.
pub fn interesting(i : u32) -> Result<bool, ()> {
if i < 42 {
return Ok(i % 2 == 0);
}
Err(todo!())
}
pub fn bar() -> Result<bool, ()> {
let numbers = vec![1, 2, 3, 4, 5, 100];
// Check if all numbers are greater than 0
let all_good = numbers.iter().all(|&x| interesting(x).unwrap_or(false));
Ok(all_good)
}
I want to avoid using unwrap and use ? instead to propagate the error (if any) to the caller.
You're looking for something like Iterator::try_all(), which doesn't exist, neither in Iterator nor in Itertools. You could use try_fold() to emulate it with fairly obvious code, but that doesn't get you short-circuiting (iteration stopping on the first false). Short-circuiting can be implemented with try_fold() using ControlFlow, but it makes the code inelegant because you can no longer use ?.
If you insist on a "functional" approach making use of iterator combinators, look no further than the find_map() solution by @yshavit. But it still requires some thinking to understand, and calls for an explanatory comment. To actually solve the problem in production code, I humbly suggest that in this case you forgo a functional solution, and write a simple and readable for loop, like this:
pub fn bar() -> Result<bool, ()> {
let numbers = vec![1, 2, 3, 4, 5, 100];
for &x in &numbers {
if !interesting(x)? {
return Ok(false);
}
}
Ok(true)
}
Playground
You could use find_map, where the f returns an Option<Result<bool>> where:
Some is a short-circuited answer: either false or an errorNone means "this element is matching, so keep looking"Then, you just need to (a) write a closure to describe that Some(true) doesn't short-circuit, and (b) end it with an unwrap_or to turn a None intoSome(true). That's not quite as convenient as what you may be looking for, but it's not bad:
pub fn bar(numbers: &[u32]) -> Result<bool, ()> {
numbers.iter().find_map(|&x| match interesting(x) {
Ok(true) => None, // element matches, so keep looking
other => Some(other), // short-circuit a false or an error
}).unwrap_or(Ok(true)) // no elements were false or error, so it's true
}
See it on Rust Playground
(If you wanted try_any instead of try_all, that would work much the same way, just with both trues flipped to falses.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With