โข 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
// 1008: Option to Result Conversion
// Convert Option<T> to Result<T, E> with ok_or / ok_or_else
use std::collections::HashMap;
fn build_users() -> HashMap<String, (String, u32)> {
let mut m = HashMap::new();
m.insert("Alice".into(), ("alice@ex.com".into(), 30));
m.insert("Bob".into(), ("bob@ex.com".into(), 17));
m
}
// Approach 1: ok_or โ eager error value
fn find_user_eager<'a>(users: &'a HashMap<String, (String, u32)>, name: &str) -> Result<&'a (String, u32), String> {
users.get(name).ok_or(format!("user not found: {}", name))
}
// Approach 2: ok_or_else โ lazy error (avoids allocation if Some)
fn find_user_lazy<'a>(users: &'a HashMap<String, (String, u32)>, name: &str) -> Result<&'a (String, u32), String> {
users.get(name).ok_or_else(|| format!("user not found: {}", name))
}
// Approach 3: Chaining Option->Result in a pipeline
fn find_and_validate(
users: &HashMap<String, (String, u32)>,
name: &str,
min_age: u32,
) -> Result<(String, u32), String> {
users
.get(name)
.ok_or_else(|| format!("user not found: {}", name))
.and_then(|(email, age)| {
if *age >= min_age {
Ok((email.clone(), *age))
} else {
Err(format!("{} is too young ({} < {})", name, age, min_age))
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ok_or_found() {
let users = build_users();
let result = find_user_eager(&users, "Alice");
assert!(result.is_ok());
assert_eq!(result.unwrap().1, 30);
}
#[test]
fn test_ok_or_not_found() {
let users = build_users();
let result = find_user_eager(&users, "Unknown");
assert_eq!(result.unwrap_err(), "user not found: Unknown");
}
#[test]
fn test_ok_or_else_lazy() {
let users = build_users();
assert!(find_user_lazy(&users, "Bob").is_ok());
assert!(find_user_lazy(&users, "Nobody").is_err());
}
#[test]
fn test_validate_success() {
let users = build_users();
let result = find_and_validate(&users, "Alice", 18);
assert_eq!(result.unwrap(), ("alice@ex.com".into(), 30));
}
#[test]
fn test_validate_too_young() {
let users = build_users();
let result = find_and_validate(&users, "Bob", 18);
assert!(result.unwrap_err().contains("too young"));
}
#[test]
fn test_validate_not_found() {
let users = build_users();
let result = find_and_validate(&users, "Nobody", 18);
assert!(result.unwrap_err().contains("not found"));
}
#[test]
fn test_option_methods() {
// Direct Option -> Result conversions
assert_eq!(Some(42).ok_or("missing"), Ok(42));
assert_eq!(None::<i32>.ok_or("missing"), Err("missing"));
// Result -> Option conversions
assert_eq!(Ok::<i32, &str>(42).ok(), Some(42));
assert_eq!(Err::<i32, &str>("fail").ok(), None);
}
}
(* 1008: Option to Result Conversion *)
(* Converting Option to Result with error context *)
(* Approach 1: Pattern matching *)
let find_user_manual users name =
match List.assoc_opt name users with
| Some user -> Ok user
| None -> Error (Printf.sprintf "user not found: %s" name)
(* Approach 2: Helper function (like ok_or) *)
let ok_or error = function
| Some v -> Ok v
| None -> Error error
let ok_or_else error_fn = function
| Some v -> Ok v
| None -> Error (error_fn ())
let find_user users name =
List.assoc_opt name users
|> ok_or_else (fun () -> Printf.sprintf "user not found: %s" name)
(* Approach 3: Chaining Option-to-Result in a pipeline *)
let find_and_validate users name min_age =
List.assoc_opt name users
|> ok_or_else (fun () -> Printf.sprintf "user not found: %s" name)
|> Result.bind (fun (_, age) ->
if age >= min_age then Ok (name, age)
else Error (Printf.sprintf "%s is too young (%d < %d)" name age min_age))
let users = [("Alice", ("alice@ex.com", 30)); ("Bob", ("bob@ex.com", 17))]
let test_manual () =
assert (find_user_manual users "Alice" = Ok ("alice@ex.com", 30));
assert (find_user_manual users "Unknown" = Error "user not found: Unknown");
Printf.printf " Approach 1 (manual): passed\n"
let test_ok_or () =
assert (ok_or "missing" (Some 42) = Ok 42);
assert (ok_or "missing" None = Error "missing");
assert (find_user users "Alice" = Ok ("alice@ex.com", 30));
Printf.printf " Approach 2 (ok_or helper): passed\n"
let test_chain () =
assert (find_and_validate users "Alice" 18 = Ok ("Alice", 30));
(match find_and_validate users "Bob" 18 with
| Error msg -> assert (String.length msg > 0)
| Ok _ -> assert false);
(match find_and_validate users "Unknown" 18 with
| Error msg -> assert (msg = "user not found: Unknown")
| Ok _ -> assert false);
Printf.printf " Approach 3 (chained pipeline): passed\n"
let () =
Printf.printf "Testing option to result:\n";
test_manual ();
test_ok_or ();
test_chain ();
Printf.printf "โ All tests passed\n"
| Aspect | OCaml | Rust | ||
|---|---|---|---|---|
| Option to Result | Custom helper or match | `.ok_or()` / `.ok_or_else()` | ||
| Result to Option | Custom helper | `.ok()` / `.err()` | ||
| Lazy error | `fun () -> ...` thunk | `\ | \ | ...` closure |
| Chaining | `\ | >` pipeline | `.method()` chain | |
| In stdlib | Since 4.08 (partial) | Always available |