โข Option
โข Result
โข 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
โข Option
โข Result
โข 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
// 1009: Collecting Results
// Iterator<Item=Result<T,E>> -> Result<Vec<T>, E> via collect()
fn parse_int(s: &str) -> Result<i64, String> {
s.parse::<i64>().map_err(|_| format!("bad: {}", s))
}
// Approach 1: collect() โ the magic of FromIterator for Result
fn parse_all(inputs: &[&str]) -> Result<Vec<i64>, String> {
inputs.iter().map(|s| parse_int(s)).collect()
}
// Approach 2: Manual fold for clarity
fn parse_all_manual(inputs: &[&str]) -> Result<Vec<i64>, String> {
let mut results = Vec::new();
for s in inputs {
results.push(parse_int(s)?);
}
Ok(results)
}
// Approach 3: Using try_fold
fn parse_all_fold(inputs: &[&str]) -> Result<Vec<i64>, String> {
inputs.iter().try_fold(Vec::new(), |mut acc, s| {
acc.push(parse_int(s)?);
Ok(acc)
})
}
fn main() {
let good = &["1", "2", "3"];
let bad = &["1", "abc", "3"];
println!("Good: {:?}", parse_all(good));
println!("Bad: {:?}", parse_all(bad));
println!("Run `cargo test` to verify all examples.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collect_all_ok() {
assert_eq!(parse_all(&["1", "2", "3"]), Ok(vec![1, 2, 3]));
}
#[test]
fn test_collect_first_error() {
let result = parse_all(&["1", "abc", "3"]);
assert_eq!(result, Err("bad: abc".to_string()));
}
#[test]
fn test_collect_empty() {
assert_eq!(parse_all(&[]), Ok(vec![]));
}
#[test]
fn test_manual_matches_collect() {
let inputs = &["10", "20", "30"];
assert_eq!(parse_all(inputs), parse_all_manual(inputs));
let bad = &["10", "x"];
assert!(parse_all_manual(bad).is_err());
}
#[test]
fn test_fold_matches_collect() {
let inputs = &["5", "10", "15"];
assert_eq!(parse_all(inputs), parse_all_fold(inputs));
}
#[test]
fn test_short_circuit_behavior() {
// collect() on Result short-circuits at first Err
let mut count = 0;
let result: Result<Vec<i64>, String> = ["1", "bad", "3"]
.iter()
.map(|s| {
count += 1;
parse_int(s)
})
.collect();
assert!(result.is_err());
// Iterator is lazy โ may stop at error
assert!(count <= 3);
}
#[test]
fn test_single_element() {
assert_eq!(parse_all(&["42"]), Ok(vec![42]));
assert!(parse_all(&["xyz"]).is_err());
}
}
(* 1009: Collecting Results *)
(* Turning a list of results into a result of list *)
let parse_int s =
match int_of_string_opt s with
| Some n -> Ok n
| None -> Error (Printf.sprintf "bad: %s" s)
(* Approach 1: Manual fold โ short-circuits on first error *)
let collect_results results =
List.fold_left (fun acc r ->
match acc, r with
| Error e, _ -> Error e (* already failed *)
| _, Error e -> Error e (* new failure *)
| Ok xs, Ok x -> Ok (xs @ [x]) (* both ok, accumulate *)
) (Ok []) results
(* Approach 2: Tail-recursive with early exit *)
let rec sequence_results acc = function
| [] -> Ok (List.rev acc)
| Ok x :: rest -> sequence_results (x :: acc) rest
| Error e :: _ -> Error e
let sequence rs = sequence_results [] rs
(* Approach 3: map then sequence *)
let traverse f xs =
List.map f xs |> sequence
let test_fold () =
let inputs = ["1"; "2"; "3"] in
let results = List.map parse_int inputs in
assert (collect_results results = Ok [1; 2; 3]);
let bad_inputs = ["1"; "abc"; "3"] in
let bad_results = List.map parse_int bad_inputs in
(match collect_results bad_results with
| Error e -> assert (e = "bad: abc")
| Ok _ -> assert false);
Printf.printf " Approach 1 (fold): passed\n"
let test_sequence () =
assert (sequence [Ok 1; Ok 2; Ok 3] = Ok [1; 2; 3]);
(match sequence [Ok 1; Error "fail"; Ok 3] with
| Error "fail" -> ()
| _ -> assert false);
Printf.printf " Approach 2 (sequence): passed\n"
let test_traverse () =
assert (traverse parse_int ["10"; "20"; "30"] = Ok [10; 20; 30]);
assert (traverse parse_int [] = Ok []);
(match traverse parse_int ["1"; "x"] with
| Error _ -> ()
| Ok _ -> assert false);
Printf.printf " Approach 3 (traverse): passed\n"
let () =
Printf.printf "Testing collecting results:\n";
test_fold ();
test_sequence ();
test_traverse ();
Printf.printf "โ All tests passed\n"
| Aspect | OCaml | Rust |
|---|---|---|
| Built-in | No (manual `sequence`) | Yes (`collect()`) |
| Short-circuits | Must implement | Automatic |
| Type inference | N/A | Drives `collect()` target |
| Traverse (map+collect) | Manual `traverse` | `.map(f).collect()` |
| Empty input | `Ok []` | `Ok(vec![])` |