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")