โข 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
let inputs = ["1", "two", "3", "four", "5"];
// Step 1: map to Result
let results: Vec<Result<i32, &str>> = inputs.iter()
.map(|s| s.parse::<i32>().map_err(|_| *s))
.collect();
// Step 2: partition into Ok and Err groups (single pass, processes ALL)
let (successes, failures): (Vec<_>, Vec<_>) = results
.into_iter()
.partition(Result::is_ok);
// Step 3: unwrap each group (safe โ partition guarantees type)
let nums: Vec<i32> = successes.into_iter().flatten().collect();
// โ .flatten() on Result<T, E> yields the Ok value
let bad: Vec<&str> = failures.into_iter().map(|r| r.unwrap_err()).collect();
println!("Parsed: {:?}", nums); // [1, 3, 5]
println!("Unparseable: {:?}", bad); // ["two", "four"]
// Practical: batch report
println!("{} succeeded, {} failed", nums.len(), bad.len());
The `.flatten()` trick works because `IntoIterator for Result<T, E>` yields zero items for `Err` and one item for `Ok`. After partition, you know all items in `successes` are `Ok`, so `flatten()` safely extracts the values.
| Concept | OCaml | Rust |
|---|---|---|
| Split into two lists | Manual fold into pair | `partition(Result::is_ok)` |
| Short-circuits? | No (manual) | No โ all items processed |
| vs. collect::\<Result\> | Stops at first Err | Never stops โ collects all |
| Extract values after | Manual pattern match | `.flatten()` on Ok group; `.map(unwrap_err)` on Err group |
//! # Splitting Ok/Err with partition()
//!
//! `partition(Result::is_ok)` collects ALL successes and ALL failures in one pass.
/// Partition results into successes and failures
pub fn partition_results<T: std::fmt::Debug, E: std::fmt::Debug>(results: Vec<Result<T, E>>) -> (Vec<T>, Vec<E>) {
let (oks, errs): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
let ok_vals: Vec<T> = oks.into_iter().map(|r| r.unwrap()).collect();
let err_vals: Vec<E> = errs.into_iter().map(|r| r.unwrap_err()).collect();
(ok_vals, err_vals)
}
/// Parse all strings, collecting both successes and failures
pub fn parse_all_report(inputs: &[&str]) -> (Vec<i32>, Vec<String>) {
let results: Vec<Result<i32, String>> = inputs
.iter()
.map(|s| s.parse::<i32>().map_err(|_| s.to_string()))
.collect();
partition_results(results)
}
/// Alternative using fold for more control
pub fn partition_fold<T, E>(results: Vec<Result<T, E>>) -> (Vec<T>, Vec<E>) {
results.into_iter().fold((vec![], vec![]), |(mut oks, mut errs), r| {
match r {
Ok(v) => oks.push(v),
Err(e) => errs.push(e),
}
(oks, errs)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partition_results() {
let v: Vec<Result<i32, &str>> = vec![Ok(1), Err("bad"), Ok(3)];
let (oks, errs) = partition_results(v);
assert_eq!(oks, vec![1, 3]);
assert_eq!(errs, vec!["bad"]);
}
#[test]
fn test_partition_all_ok() {
let v: Vec<Result<i32, &str>> = vec![Ok(1), Ok(2)];
let (oks, errs) = partition_results(v);
assert_eq!(oks, vec![1, 2]);
assert!(errs.is_empty());
}
#[test]
fn test_parse_all_report() {
let (nums, bad) = parse_all_report(&["1", "two", "3", "four"]);
assert_eq!(nums, vec![1, 3]);
assert_eq!(bad, vec!["two", "four"]);
}
#[test]
fn test_partition_fold() {
let v: Vec<Result<i32, &str>> = vec![Ok(1), Err("x"), Ok(2)];
let (oks, errs) = partition_fold(v);
assert_eq!(oks, vec![1, 2]);
assert_eq!(errs, vec!["x"]);
}
#[test]
fn test_empty_input() {
let v: Vec<Result<i32, &str>> = vec![];
let (oks, errs) = partition_results(v);
assert!(oks.is_empty());
assert!(errs.is_empty());
}
}
(* 304. Splitting Ok/Err with partition() - OCaml *)
let partition_results lst =
List.fold_right (fun r (oks, errs) ->
match r with
| Ok v -> (v :: oks, errs)
| Error e -> (oks, e :: errs)
) lst ([], [])
let () =
let results = [Ok 1; Error "bad1"; Ok 3; Error "bad2"; Ok 5] in
let (oks, errs) = partition_results results in
Printf.printf "Ok values: %s\n"
(String.concat ", " (List.map string_of_int oks));
Printf.printf "Errors: %s\n"
(String.concat ", " errs);
let inputs = ["1"; "two"; "3"; "four"; "5"] in
let parsed = List.map (fun s ->
match int_of_string_opt s with
| Some n -> Ok n | None -> Error s
) inputs in
let (nums, bad_strs) = partition_results parsed in
Printf.printf "Parsed: %s\nFailed: %s\n"
(String.concat ", " (List.map string_of_int nums))
(String.concat ", " bad_strs)