โข 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
// 1010: Partition Results
// Separate Ok and Err values using Iterator::partition
fn parse_int(s: &str) -> Result<i64, String> {
s.parse::<i64>().map_err(|_| format!("bad: {}", s))
}
// Approach 1: partition into two Vecs of Results, then unwrap
fn partition_results(inputs: &[&str]) -> (Vec<i64>, Vec<String>) {
let (oks, errs): (Vec<Result<i64, String>>, Vec<Result<i64, String>>) =
inputs.iter().map(|s| parse_int(s)).partition(Result::is_ok);
(
oks.into_iter().map(Result::unwrap).collect(),
errs.into_iter().map(Result::unwrap_err).collect(),
)
}
// Approach 2: Single fold into two accumulators
fn partition_fold(inputs: &[&str]) -> (Vec<i64>, Vec<String>) {
inputs.iter().map(|s| parse_int(s)).fold(
(Vec::new(), Vec::new()),
|(mut oks, mut errs), result| {
match result {
Ok(v) => oks.push(v),
Err(e) => errs.push(e),
}
(oks, errs)
},
)
}
// Approach 3: Using filter_map for just one side
fn only_successes(inputs: &[&str]) -> Vec<i64> {
inputs.iter().filter_map(|s| parse_int(s).ok()).collect()
}
fn only_errors(inputs: &[&str]) -> Vec<String> {
inputs.iter().filter_map(|s| parse_int(s).err()).collect()
}
fn main() {
let inputs = &["1", "abc", "3", "def", "5"];
let (oks, errs) = partition_results(inputs);
println!("Successes: {:?}", oks);
println!("Failures: {:?}", errs);
println!("Run `cargo test` to verify all examples.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partition_mixed() {
let (oks, errs) = partition_results(&["1", "abc", "3", "def", "5"]);
assert_eq!(oks, vec![1, 3, 5]);
assert_eq!(errs, vec!["bad: abc", "bad: def"]);
}
#[test]
fn test_partition_all_ok() {
let (oks, errs) = partition_results(&["1", "2", "3"]);
assert_eq!(oks, vec![1, 2, 3]);
assert!(errs.is_empty());
}
#[test]
fn test_partition_all_err() {
let (oks, errs) = partition_results(&["a", "b", "c"]);
assert!(oks.is_empty());
assert_eq!(errs.len(), 3);
}
#[test]
fn test_fold_matches_partition() {
let inputs = &["1", "abc", "3"];
assert_eq!(partition_results(inputs), partition_fold(inputs));
}
#[test]
fn test_filter_map_successes() {
assert_eq!(only_successes(&["1", "x", "3"]), vec![1, 3]);
}
#[test]
fn test_filter_map_errors() {
assert_eq!(only_errors(&["1", "x", "3"]), vec!["bad: x"]);
}
#[test]
fn test_empty_input() {
let (oks, errs) = partition_results(&[]);
assert!(oks.is_empty());
assert!(errs.is_empty());
}
}
(* 1010: Partition Results *)
(* Separate Ok and Err values from a list of results *)
let parse_int s =
match int_of_string_opt s with
| Some n -> Ok n
| None -> Error (Printf.sprintf "bad: %s" s)
(* Approach 1: List.partition with is_ok helper *)
let is_ok = function Ok _ -> true | Error _ -> false
let partition_results results =
let oks, errs = List.partition is_ok results in
let unwrap_ok = function Ok v -> v | Error _ -> assert false in
let unwrap_err = function Error e -> e | Ok _ -> assert false in
(List.map unwrap_ok oks, List.map unwrap_err errs)
(* Approach 2: Single fold separating into two accumulators *)
let partition_fold results =
let oks, errs = List.fold_left (fun (oks, errs) r ->
match r with
| Ok v -> (v :: oks, errs)
| Error e -> (oks, e :: errs)
) ([], []) results in
(List.rev oks, List.rev errs)
let test_partition () =
let inputs = ["1"; "abc"; "3"; "def"; "5"] in
let results = List.map parse_int inputs in
let oks, errs = partition_results results in
assert (oks = [1; 3; 5]);
assert (errs = ["bad: abc"; "bad: def"]);
Printf.printf " Approach 1 (partition + unwrap): passed\n"
let test_fold () =
let inputs = ["1"; "abc"; "3"; "def"; "5"] in
let results = List.map parse_int inputs in
let oks, errs = partition_fold results in
assert (oks = [1; 3; 5]);
assert (errs = ["bad: abc"; "bad: def"]);
(* All ok *)
let all_ok = List.map parse_int ["1"; "2"; "3"] in
let oks, errs = partition_fold all_ok in
assert (oks = [1; 2; 3]);
assert (errs = []);
Printf.printf " Approach 2 (fold): passed\n"
let () =
Printf.printf "Testing partition results:\n";
test_partition ();
test_fold ();
Printf.printf "โ All tests passed\n"
| Aspect | OCaml | Rust |
|---|---|---|
| Partition | `List.partition is_ok` | `iter.partition(Result::is_ok)` |
| Unwrap after | Manual pattern match | `Result::unwrap` (safe post-partition) |
| One-sided | `List.filter_map` | `filter_map(Result::ok)` |
| Performance | Two passes (partition + map) | Same |
| Use case | Collect all errors for reporting | Same |