Option<T> and Result<T, E> in Depth
Collecting Result Values
When an iterator yields Result<T, E> values, the FromIterator implementation on Result<V, E> (where V: FromIterator<T>) lets you collect the entire sequence into Result<Vec<T>, E>—short-circuiting on the first Err(e)—or you can partition/collect all errors when full batch diagnostics are needed.
Four Ways to Consume an Iterator<Item = Result<T, E>>
| Goal | Idiom | Behavior on First Err(e) |
|---|---|---|
| 1. Fail fast on first error | iter.collect::<Result<Vec<T>, E>>() |
Stops polling immediately and returns Err(e) |
2. Validate all without allocating a Vec |
iter.collect::<Result<(), E>>() (when Item = Result<(), E>) |
Runs side effects/checks and stops at the first Err(e) with 0 heap allocations |
3. Keep only Ok(T), ignore errors |
iter.flatten().collect::<Vec<T>>() |
Discards Err(_) and collects all Ok(v) values |
4. Separate all Ok(T) and all Err(E) |
iter.partition::<Vec<_>, _>(Result::is_ok) |
Evaluates every item and splits successes from failures |
1. Fail-Fast Collection into Result<Vec<T>, E>
use std::num::ParseIntError;
fn parse_numbers(items: &[&str]) -> Result<Vec<i32>, ParseIntError> {
items
.iter()
.map(|s| {
println!(" parsing '{s}'");
s.parse::<i32>()
})
.collect()
}
fn main() {
println!("Run 1 (all valid):");
println!(" -> {:?}", parse_numbers(&["10", "20", "30"]));
println!("Run 2 (short-circuits at 'bad'; '30' is never parsed!):");
println!(" -> {:?}", parse_numbers(&["10", "bad", "30"]));
}
Run 1 (all valid):
parsing '10'
parsing '20'
parsing '30'
-> Ok([10, 20, 30])
Run 2 (short-circuits at 'bad'; '30' is never parsed!):
parsing '10'
parsing 'bad'
-> Err(ParseIntError { kind: InvalidDigit })
2. Zero-Allocation Validation with Result<(), E>
Because the unit type () implements FromIterator<()> as a no-op, collecting an Iterator<Item = Result<(), E>> into Result<(), E> executes each fallible check and short-circuits on the first error without allocating a container:
fn validate_ports(ports: &[u16]) -> Result<(), String> {
ports
.iter()
.map(|&p| {
if p >= 1024 {
Ok(())
} else {
Err(format!("privileged port {p} is not allowed"))
}
})
.collect()
}
fn main() {
println!("Valid ports: {:?}", validate_ports(&[8080, 8443, 9000]));
println!("Invalid ports: {:?}", validate_ports(&[8080, 80, 9000]));
}
Valid ports: Ok(())
Invalid ports: Err("privileged port 80 is not allowed")