โข 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
// 1007: Result Combinators
// and_then, or_else, map, map_err, unwrap_or_else
fn parse_int(s: &str) -> Result<i64, String> {
s.parse::<i64>().map_err(|e| format!("not an int: {} ({})", s, e))
}
fn double_if_positive(n: i64) -> Result<i64, String> {
if n > 0 {
Ok(n * 2)
} else {
Err("must be positive".into())
}
}
// Approach 1: Chaining with and_then (flatmap/bind)
fn process_chain(s: &str) -> Result<String, String> {
parse_int(s)
.and_then(double_if_positive)
.map(|n| n.to_string())
}
// Approach 2: Using map, map_err, or_else, unwrap_or_else
fn process_with_fallback(s: &str) -> String {
parse_int(s)
.and_then(double_if_positive)
.map(|n| n.to_string())
.map_err(|e| format!("FALLBACK: {}", e))
.unwrap_or_else(|e| e)
}
fn process_or_else(s: &str) -> Result<i64, String> {
parse_int(s)
.and_then(double_if_positive)
.or_else(|_| Ok(0)) // fallback to 0 on any error
}
fn main() {
println!("chain '5': {:?}", process_chain("5"));
println!("chain '-3': {:?}", process_chain("-3"));
println!("fallback 'abc': {}", process_with_fallback("abc"));
println!("or_else '-1': {:?}", process_or_else("-1"));
println!("Run `cargo test` to verify all examples.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_and_then_success() {
assert_eq!(process_chain("5"), Ok("10".to_string()));
}
#[test]
fn test_and_then_negative() {
assert_eq!(process_chain("-3"), Err("must be positive".to_string()));
}
#[test]
fn test_and_then_parse_fail() {
assert!(process_chain("abc").is_err());
}
#[test]
fn test_map() {
let result: Result<i64, String> = Ok(5);
assert_eq!(result.map(|n| n * 2), Ok(10));
}
#[test]
fn test_map_err() {
let result: Result<i64, &str> = Err("low");
assert_eq!(result.map_err(|e| e.to_uppercase()), Err("LOW".to_string()));
}
#[test]
fn test_or_else() {
assert_eq!(process_or_else("-1"), Ok(0));
assert_eq!(process_or_else("5"), Ok(10));
}
#[test]
fn test_unwrap_or_else() {
let result: Result<i64, String> = Err("fail".into());
assert_eq!(result.unwrap_or_else(|_| 99), 99);
let result: Result<i64, String> = Ok(42);
assert_eq!(result.unwrap_or_else(|_| 99), 42);
}
#[test]
fn test_fallback_string() {
assert_eq!(process_with_fallback("5"), "10");
assert!(process_with_fallback("abc").starts_with("FALLBACK"));
}
}
(* 1007: Result Combinators *)
(* Functional transforms on Result values *)
(* OCaml Result module combinators *)
let map f = function Ok v -> Ok (f v) | Error e -> Error e
let map_error f = function Ok v -> Ok v | Error e -> Error (f e)
let bind f = function Ok v -> f v | Error e -> Error e (* and_then *)
(* Approach 1: Manual pattern matching *)
let parse_int s =
match int_of_string_opt s with
| Some n -> Ok n
| None -> Error (Printf.sprintf "not an int: %s" s)
let double_if_positive n =
if n > 0 then Ok (n * 2)
else Error "must be positive"
let process_manual s =
match parse_int s with
| Error e -> Error e
| Ok n ->
match double_if_positive n with
| Error e -> Error e
| Ok v -> Ok (string_of_int v)
(* Approach 2: Using combinators *)
let process_combinators s =
parse_int s
|> bind double_if_positive
|> map string_of_int
(* or_else equivalent *)
let or_else f = function
| Ok v -> Ok v
| Error e -> f e
let with_default default = function
| Ok v -> v
| Error _ -> default
let test_manual () =
assert (process_manual "5" = Ok "10");
assert (process_manual "-3" = Error "must be positive");
assert (process_manual "abc" = Error "not an int: abc");
Printf.printf " Approach 1 (manual matching): passed\n"
let test_combinators () =
assert (process_combinators "5" = Ok "10");
assert (process_combinators "-3" = Error "must be positive");
(* map_error *)
let r = map_error String.uppercase_ascii (Error "low") in
assert (r = Error "LOW");
(* or_else *)
let r = or_else (fun _ -> Ok 0) (Error "fail") in
assert (r = Ok 0);
(* unwrap_or / with_default *)
assert (with_default 99 (Error "fail") = 99);
assert (with_default 99 (Ok 42) = 42);
Printf.printf " Approach 2 (combinators): passed\n"
let () =
Printf.printf "Testing result combinators:\n";
test_manual ();
test_combinators ();
Printf.printf "โ All tests passed\n"
| Combinator | OCaml | Rust |
|---|---|---|
| map | `Result.map f r` | `r.map(f)` |
| flatmap/bind | `Result.bind r f` | `r.and_then(f)` |
| map error | custom `map_error` | `r.map_err(f)` |
| fallback | custom `or_else` | `r.or_else(f)` |
| default | `Result.value r ~default` | `r.unwrap_or(v)` |
| lazy default | custom | `r.unwrap_or_else(f)` |