Need to accumulate values from an iterator but bail on the first error? try_fold gives you the power of fold with the early-exit behavior of ?.
The problem
You’re parsing a list of strings into numbers and summing them. With fold, you have no clean way to short-circuit on a parse failure:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| fn main() {
let inputs = vec!["10", "20", "oops", "40"];
// This doesn't compile — fold expects the same type every iteration
// and there's no way to bail early
let mut total = 0i64;
let mut error = None;
for s in &inputs {
match s.parse::<i64>() {
Ok(n) => total += n,
Err(e) => {
error = Some(e);
break;
}
}
}
assert!(error.is_some());
assert_eq!(total, 30); // partial sum before the error
}
|
It works, but you’ve traded iterator chains for mutable state and a manual loop.
The clean way
try_fold takes an initial accumulator and a closure that returns Result<Acc, E> (or any type implementing Try). It stops at the first Err and returns it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| fn sum_parsed(inputs: &[&str]) -> Result<i64, std::num::ParseIntError> {
inputs.iter().try_fold(0i64, |acc, s| {
let n = s.parse::<i64>()?;
Ok(acc + n)
})
}
fn main() {
// All valid — returns the sum
assert_eq!(sum_parsed(&["10", "20", "30"]), Ok(60));
// Stops at "oops", never touches "40"
assert!(sum_parsed(&["10", "20", "oops", "40"]).is_err());
}
|
No mutable variables, no manual loop, no tracking partial state. The ? inside the closure does exactly what you’d expect.
It works with Option too
try_fold works with any Try type. With Option, it stops at the first None:
1
2
3
4
5
6
7
8
9
10
11
12
13
| fn main() {
let values = vec![Some(1), Some(2), Some(3)];
let sum = values.iter().try_fold(0, |acc, opt| {
opt.map(|n| acc + n)
});
assert_eq!(sum, Some(6));
let values = vec![Some(1), None, Some(3)];
let sum = values.iter().try_fold(0, |acc, opt| {
opt.map(|n| acc + n)
});
assert_eq!(sum, None); // stopped at None
}
|
Bonus: try_for_each
If you don’t need an accumulator and just want to run a fallible operation on each element, try_for_each is the shorthand:
1
2
3
4
5
6
7
8
9
10
11
| fn validate_all(inputs: &[&str]) -> Result<(), std::num::ParseIntError> {
inputs.iter().try_for_each(|s| {
s.parse::<i64>()?;
Ok(())
})
}
fn main() {
assert!(validate_all(&["1", "2", "3"]).is_ok());
assert!(validate_all(&["1", "nope", "3"]).is_err());
}
|
Both methods are lazy — they only consume as many elements as needed. When your fold can fail, reach for try_fold instead of a manual loop.