๐Ÿฆ€ Functional Rust
๐ŸŽฌ Error Handling in Rust Option, Result, the ? operator, and combinators.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Option represents a value that may or may not exist โ€” Some(value) or None

โ€ข Result represents success (Ok) or failure (Err) โ€” no exceptions needed

โ€ข The ? operator propagates errors up the call stack concisely

โ€ข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations

โ€ข The compiler forces you to handle every error case โ€” no silent failures

303: Collecting Iterator<Result<T>> into Result<Vec<T>>

Difficulty: 2 Level: Intermediate Transform a sequence of fallible operations into an all-or-nothing result in one expression.

The Problem This Solves

You have a list of strings to parse, a list of files to read, or a list of records to validate. Each individual operation returns `Result<T, E>`. You want the full list if everything succeeds, or the first error if anything fails. The naive approach is a `for` loop with early return โ€” verbose and hard to compose with the rest of your iterator pipeline. Rust's `FromIterator` implementation for `Result` solves this. When you write `.collect::<Result<Vec<T>, E>>()`, the iterator stops at the first `Err` and returns it โ€” or returns `Ok(Vec)` containing all the values if every item was `Ok`. It's a short-circuit fold that's built into the type system. This matters because it composes cleanly with the rest of the iterator toolkit. You can `map`, `filter`, and `flat_map` before the collect โ€” the whole pipeline runs on the happy path, and any error short-circuits at collect time.

The Intuition

`collect::<Result<Vec<T>, E>>()` is a short-circuit fold: accumulate all `Ok` values into a `Vec`, but stop and return the first `Err` encountered.

How It Works in Rust

// The type annotation on collect() is what triggers this behavior
let inputs = ["1", "2", "3", "4"];
let parsed: Result<Vec<i32>, _> = inputs.iter()
 .map(|s| s.parse::<i32>())  // Iterator<Item = Result<i32, ParseIntError>>
 .collect();                  // Result<Vec<i32>, ParseIntError> โ€” all or nothing

// Ok if everything succeeds
assert_eq!(parsed, Ok(vec![1, 2, 3, 4]));

// Err on first failure โ€” later items aren't processed
let bad_inputs = ["1", "two", "3"];
let result: Result<Vec<i32>, _> = bad_inputs.iter()
 .map(|s| s.parse::<i32>())
 .collect();
// => Err(ParseIntError for "two") โ€” "3" was never attempted

// The turbofish form when the type can't be inferred:
let result = inputs.iter()
 .map(|s| s.parse::<i32>())
 .collect::<Result<Vec<_>, _>>();
The short-circuit behavior is important: if you have expensive operations after the first bad item, they won't run. If you need to process all items and collect all errors, use `partition` (example 304) instead.

What This Unlocks

Key Differences

ConceptOCamlRust
All-or-nothing foldManual fold with early return`.collect::<Result<Vec<_>, _>>()`
Short-circuit on errorManualAutomatic โ€” first `Err` stops iteration
Type annotationN/ARequired to select the `Result`-collecting `FromIterator`
vs. collect all errorsManual two-passUse `partition(Result::is_ok)` instead
//! 303. Collecting Iterator<Result<T>> into Result<Vec<T>>
//!
//! `collect::<Result<Vec<T>,E>>()` short-circuits on first Err.

fn main() {
    // All Ok -> Ok(Vec)
    let good: Vec<Result<i32, &str>> = vec![Ok(1), Ok(2), Ok(3)];
    let result: Result<Vec<i32>, &str> = good.into_iter().collect();
    println!("All ok: {:?}", result);

    // One Err -> Err (short-circuits)
    let bad: Vec<Result<i32, &str>> = vec![Ok(1), Ok(2), Err("oops"), Ok(4)];
    let result: Result<Vec<i32>, &str> = bad.into_iter().collect();
    println!("With error: {:?}", result);

    // Practical: parse all strings
    let inputs = ["1", "2", "3", "4"];
    let parsed: Result<Vec<i32>, _> = inputs.iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Parse all: {:?}", parsed);

    // Fail case
    let bad_inputs = ["1", "two", "3"];
    let parsed_bad: Result<Vec<i32>, _> = bad_inputs.iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Parse with bad: {:?}", parsed_bad);

    // Collect and process only if all succeed
    if let Ok(numbers) = (["10", "20", "30"]).iter()
        .map(|s| s.parse::<i32>())
        .collect::<Result<Vec<_>, _>>()
    {
        let sum: i32 = numbers.iter().sum();
        println!("Sum of all: {}", sum);
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_collect_all_ok() {
        let result: Result<Vec<i32>, &str> = vec![Ok(1), Ok(2), Ok(3)].into_iter().collect();
        assert_eq!(result, Ok(vec![1, 2, 3]));
    }

    #[test]
    fn test_collect_with_err() {
        let result: Result<Vec<i32>, &str> = vec![Ok(1), Err("bad"), Ok(3)].into_iter().collect();
        assert_eq!(result, Err("bad"));
    }

    #[test]
    fn test_collect_parse_ints() {
        let ok: Result<Vec<i32>, _> = ["1","2","3"].iter().map(|s| s.parse::<i32>()).collect();
        assert_eq!(ok.unwrap(), vec![1, 2, 3]);
    }

    #[test]
    fn test_collect_parse_fails() {
        let err: Result<Vec<i32>, _> = ["1","x"].iter().map(|s| s.parse::<i32>()).collect();
        assert!(err.is_err());
    }
}
(* 303. Collecting Iterator<Result<T>> into Result<Vec<T>> - OCaml *)

let collect_results lst =
  let rec aux acc = function
    | [] -> Ok (List.rev acc)
    | Ok v :: rest -> aux (v :: acc) rest
    | Error e :: _ -> Error e
  in
  aux [] lst

let () =
  let good = [Ok 1; Ok 2; Ok 3; Ok 4] in
  Printf.printf "All ok: %s\n"
    (match collect_results good with
     | Ok vs -> "[" ^ String.concat ", " (List.map string_of_int vs) ^ "]"
     | Error e -> "Error: " ^ e);

  let bad = [Ok 1; Ok 2; Error "oops"; Ok 4] in
  Printf.printf "With error: %s\n"
    (match collect_results bad with
     | Ok _ -> "Ok"
     | Error e -> "Error: " ^ e);

  let strs = ["1"; "2"; "abc"; "4"] in
  let parsed = List.map (fun s ->
    match int_of_string_opt s with
    | Some n -> Ok n | None -> Error ("not a number: " ^ s)
  ) strs in
  Printf.printf "Parse all: %s\n"
    (match collect_results parsed with
     | Ok vs -> "[" ^ String.concat ", " (List.map string_of_int vs) ^ "]"
     | Error e -> "Error: " ^ e)