• 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
// Result::transpose() rules:
let ok_some: Result<Option<i32>, &str> = Ok(Some(42));
let ok_none: Result<Option<i32>, &str> = Ok(None);
let err: Result<Option<i32>, &str> = Err("bad");
ok_some.transpose() // => Some(Ok(42)) — success with a value
ok_none.transpose() // => None — success with no value (becomes None)
err.transpose() // => Some(Err("bad")) — failure becomes Some(Err)
// Option::transpose() goes the other direction:
let some_ok: Option<Result<i32, &str>> = Some(Ok(5));
let some_err: Option<Result<i32, &str>> = Some(Err("fail"));
let none: Option<Result<i32, &str>> = None;
some_ok.transpose() // => Ok(Some(5)) — value present, no error
some_err.transpose() // => Err("fail") — error propagates out
none.transpose() // => Ok(None) — absent is treated as success with no value
// Practical: parse an optional config value cleanly
let config_val: Option<&str> = Some("42");
let result: Result<Option<i32>, _> = config_val
.map(|s| s.parse::<i32>()) // Option<Result<i32, ParseIntError>>
.transpose(); // Result<Option<i32>, ParseIntError>
The `Option::transpose()` direction is the more commonly useful one — it lets you use `?` on an `Option<Result<T,E>>` after transposing.
| Concept | OCaml | Rust |
|---|---|---|
| `Ok(None)` → `None` | Manual match | `Result::transpose()` |
| `Ok(Some(v))` → `Some(Ok(v))` | Manual match | `Result::transpose()` |
| `Some(Ok(v))` → `Ok(Some(v))` | Manual match | `Option::transpose()` |
| Use case | Manual unwrapping | Composing optional + fallible operations |
//! 301. Converting Result<Option<T>> into Option<Result<T>>
//!
//! `Result::transpose()` swaps `Result` and `Option` layers.
fn maybe_parse(s: Option<&str>) -> Result<Option<i32>, std::num::ParseIntError> {
match s {
None => Ok(None),
Some(s) => s.parse::<i32>().map(Some),
}
}
fn main() {
// Result::transpose()
let ok_some: Result<Option<i32>, &str> = Ok(Some(42));
let ok_none: Result<Option<i32>, &str> = Ok(None);
let err: Result<Option<i32>, &str> = Err("bad");
println!("Ok(Some(42)).transpose() = {:?}", ok_some.transpose());
println!("Ok(None).transpose() = {:?}", ok_none.transpose());
println!("Err(...).transpose() = {:?}", err.transpose());
// Option::transpose()
let some_ok: Option<Result<i32, &str>> = Some(Ok(5));
let some_err: Option<Result<i32, &str>> = Some(Err("fail"));
let none: Option<Result<i32, &str>> = None;
println!("Some(Ok(5)).transpose() = {:?}", some_ok.transpose());
println!("Some(Err).transpose() = {:?}", some_err.transpose());
println!("None.transpose() = {:?}", none.transpose());
// Practical: parse optional config value
let config_val: Option<&str> = Some("42");
let parsed: Option<Result<i32, _>> = config_val.map(|s| s.parse::<i32>());
let transposed: Result<Option<i32>, _> = parsed.transpose();
println!("Config parse transposed: {:?}", transposed);
}
#[cfg(test)]
mod tests {
#[test]
fn test_result_transpose_ok_some() {
let r: Result<Option<i32>, &str> = Ok(Some(42));
assert_eq!(r.transpose(), Some(Ok(42)));
}
#[test]
fn test_result_transpose_ok_none() {
let r: Result<Option<i32>, &str> = Ok(None);
assert_eq!(r.transpose(), None);
}
#[test]
fn test_result_transpose_err() {
let r: Result<Option<i32>, &str> = Err("bad");
assert_eq!(r.transpose(), Some(Err("bad")));
}
#[test]
fn test_option_transpose() {
let o: Option<Result<i32, &str>> = Some(Ok(5));
assert_eq!(o.transpose(), Ok(Some(5)));
}
}
(* 301. Converting Result<Option<T>> into Option<Result<T>> - OCaml *)
let transpose_result_option = function
| Ok None -> None
| Ok (Some v) -> Some (Ok v)
| Error e -> Some (Error e)
let () =
let r1 : (int option, string) result = Ok (Some 42) in
let r2 : (int option, string) result = Ok None in
let r3 : (int option, string) result = Error "bad" in
Printf.printf "Ok(Some(42)) -> %s\n"
(match transpose_result_option r1 with
| Some (Ok n) -> Printf.sprintf "Some(Ok(%d))" n
| _ -> "other");
Printf.printf "Ok(None) -> %s\n"
(match transpose_result_option r2 with None -> "None" | _ -> "Some");
Printf.printf "Err -> %s\n"
(match transpose_result_option r3 with
| Some (Error e) -> Printf.sprintf "Some(Err(%s))" e
| _ -> "other")