โข 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
// Pattern 1: collect into Result โ fail-fast
let numbers: Result<Vec<i32>, _> = ["1", "2", "bad"]
.iter()
.map(|s| s.parse::<i32>()) // each closure returns Result<i32, _>
.collect(); // short-circuits on first Err
// Pattern 2: filter_map โ silently drop failures
let valid: Vec<i32> = ["1", "bad", "3"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect(); // โ [1, 3]
// Pattern 3: closure uses ? explicitly (return type must be Result)
let doubled: Vec<Result<i32, _>> = inputs.iter()
.map(|s| -> Result<i32, _> { Ok(s.parse::<i32>()? * 2) })
.collect();
// Pattern 4: try_fold for stateful accumulation
let sum = inputs.iter().try_fold(0i32, |acc, s| {
Ok(acc + s.parse::<i32>()?)
});
| Concept | OCaml | Rust | ||
|---|---|---|---|---|
| Error in map | `List.filter_map` or `List.map` with `Result` | Multiple patterns | ||
| Fail-fast collection | Manual `fold` with early return | `.collect::<Result<Vec<_>,_>>()` | ||
| Skip errors | `List.filter_map` | `.filter_map(\ | r\ | r.ok())` |
| Early return in closure | Natural with `let*` / `Result.bind` | Closure must return `Result` for `?` | ||
| Stateful short-circuit | Recursive or `fold` with option | `.try_fold()` |
//! 307. Error propagation in closures
//!
//! `?` in closures requires the closure to return `Result`/`Option`.
fn parse_number(s: &str) -> Result<i32, String> {
s.trim().parse::<i32>().map_err(|_| format!("not a number: '{}'", s))
}
fn main() {
// Pattern 1: collect into Result<Vec> (short-circuits on first error)
let strs = ["1", "2", "3", "4"];
let numbers: Result<Vec<i32>, String> = strs.iter()
.map(|s| parse_number(s))
.collect();
println!("Pattern 1 (collect): {:?}", numbers);
// Pattern 2: filter_map for Option (silently drops failures)
let mixed = ["1", "bad", "3", "also_bad", "5"];
let valid: Vec<i32> = mixed.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
println!("Pattern 2 (filter_map): {:?}", valid);
// Pattern 3: closure returning Result (can use ?)
let results: Vec<Result<i32, _>> = mixed.iter()
.map(|s| -> Result<i32, _> {
let n = s.trim().parse::<i32>()?;
if n < 0 { return Err("negative".into()); }
Ok(n * 2)
})
.collect();
println!("Pattern 3 (collect results): {:?}", results);
// Pattern 4: try_fold for short-circuit accumulation
let sum = strs.iter().try_fold(0i32, |acc, s| -> Result<i32, String> {
Ok(acc + parse_number(s)?)
});
println!("Pattern 4 (try_fold sum): {:?}", sum);
// Pattern 5: extract to named function to use ?
fn process_all(inputs: &[&str]) -> Result<Vec<i32>, String> {
inputs.iter().map(|s| parse_number(s)).collect()
}
println!("Pattern 5 (named fn): {:?}", process_all(&["10", "20", "30"]));
println!("Pattern 5 (with err): {:?}", process_all(&["10", "bad"]));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collect_all_ok() {
let result: Result<Vec<i32>, _> = ["1","2","3"].iter()
.map(|s| parse_number(s)).collect();
assert_eq!(result.unwrap(), vec![1,2,3]);
}
#[test]
fn test_filter_map_drops_errors() {
let result: Vec<i32> = ["1","bad","3"].iter()
.filter_map(|s| s.parse::<i32>().ok()).collect();
assert_eq!(result, vec![1,3]);
}
#[test]
fn test_try_fold() {
let sum = ["1","2","3"].iter()
.try_fold(0i32, |acc, s| -> Result<i32, String> {
Ok(acc + parse_number(s)?)
});
assert_eq!(sum, Ok(6));
}
}
(* 307. Error propagation in closures - OCaml *)
let () =
let parse_all strs =
let results = List.map (fun s ->
match int_of_string_opt s with
| Some n -> Ok n
| None -> Error ("not a number: " ^ s)
) strs in
List.fold_right (fun r acc ->
match r, acc with
| Ok v, Ok vs -> Ok (v :: vs)
| Error e, _ -> Error e
| _, Error e -> Error e
) results (Ok [])
in
let good = ["1"; "2"; "3"; "4"] in
let bad = ["1"; "two"; "3"] in
Printf.printf "Good: %s\n"
(match parse_all good with Ok vs -> "[" ^ String.concat "," (List.map string_of_int vs) ^ "]" | Error e -> e);
Printf.printf "Bad: %s\n"
(match parse_all bad with Ok _ -> "Ok" | Error e -> "Error: " ^ e)