โข 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
Some(42) โ there IS a value, and it's 42
None โ there is NO value
This is equivalent to a tagged union (discriminated union in TypeScript, `Maybe` in Haskell, `Optional<T>` in Java 8+). The difference: Rust enforces it at compile time.
// Safe division: instead of panicking on /0, return None
fn safe_div(a: i64, b: i64) -> Option<i64> {
if b == 0 { None } else { Some(a / b) }
}
// Safe head: instead of panicking on empty slice, return None
fn head<T: Clone>(lst: &[T]) -> Option<T> {
lst.first().cloned()
}
Using the result โ three common patterns:
// 1. Pattern match (explicit, exhaustive)
match safe_div(10, 2) {
Some(result) => println!("Got: {}", result),
None => println!("Division by zero"),
}
// 2. unwrap_or (provide a default)
let x = safe_div(10, 0).unwrap_or(0); // x = 0
// 3. The ? operator (propagate None upward)
fn compute() -> Option<i64> {
let x = safe_div(100, 5)?; // if None, return None from compute()
let y = safe_div(x, 2)?;
Some(y + 1)
}
Never do this in production: `option.unwrap()` โ it panics on `None`. Use `unwrap_or`, `unwrap_or_else`, or `?` instead.
| Concept | OCaml | Rust | ||
|---|---|---|---|---|
| The type | `'a option` | `Option<T>` | ||
| Present value | `Some x` | `Some(x)` | ||
| Absent value | `None` | `None` | ||
| Pattern match | `match opt with \ | Some x -> ... \ | None -> ...` | `match opt { Some(x) => ..., None => ... }` |
| Default value | `Option.value opt ~default:0` | `opt.unwrap_or(0)` | ||
| Propagate None | `Option.bind` or `let*` | `?` operator |
// Option Basics โ 99 Problems #41
// Explore Option<T>: construction, pattern matching, unwrapping.
/// Safe division โ returns None on division by zero.
fn safe_div(a: i64, b: i64) -> Option<i64> {
if b == 0 { None } else { Some(a / b) }
}
/// Safe head of a list.
fn head<T: Clone>(lst: &[T]) -> Option<T> {
lst.first().cloned()
}
/// Safe last of a list.
fn last<T: Clone>(lst: &[T]) -> Option<T> {
lst.last().cloned()
}
/// Find first element satisfying predicate.
fn find<T: Clone, F: Fn(&T) -> bool>(lst: &[T], pred: F) -> Option<T> {
lst.iter().find(|x| pred(x)).cloned()
}
/// Combine two Options โ both must be Some.
fn zip_options<A: Clone, B: Clone>(a: Option<A>, b: Option<B>) -> Option<(A, B)> {
match (a, b) {
(Some(x), Some(y)) => Some((x, y)),
_ => None,
}
}
fn main() {
println!("safe_div(10, 2) = {:?}", safe_div(10, 2));
println!("safe_div(10, 0) = {:?}", safe_div(10, 0));
let nums = vec![1, 2, 3, 4, 5];
println!("head([1..5]) = {:?}", head(&nums));
println!("last([1..5]) = {:?}", last(&nums));
println!("head([]) = {:?}", head::<i32>(&[]));
println!("find >3 = {:?}", find(&nums, |&x| x > 3));
println!("find >99 = {:?}", find(&nums, |&x| x > 99));
// unwrap_or
println!("div(10,0).unwrap_or(0) = {}", safe_div(10, 0).unwrap_or(0));
// ? operator style via closure
let compute = || -> Option<i64> {
let x = safe_div(100, 5)?;
let y = safe_div(x, 2)?;
Some(y + 1)
};
println!("compute() = {:?}", compute());
println!("zip(Some(1),Some('a')) = {:?}", zip_options(Some(1), Some('a')));
println!("zip(None,Some('a')) = {:?}", zip_options::<i32,char>(None, Some('a')));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safe_div() {
assert_eq!(safe_div(10, 2), Some(5));
assert_eq!(safe_div(10, 0), None);
}
#[test]
fn test_head_last() {
assert_eq!(head(&[1, 2, 3]), Some(1));
assert_eq!(last(&[1, 2, 3]), Some(3));
assert_eq!(head::<i32>(&[]), None);
}
#[test]
fn test_find() {
assert_eq!(find(&[1, 2, 3, 4], |&x| x > 2), Some(3));
assert_eq!(find(&[1, 2, 3], |&x| x > 99), None);
}
#[test]
fn test_zip_options() {
assert_eq!(zip_options(Some(1), Some("hello")), Some((1, "hello")));
assert_eq!(zip_options::<i32, &str>(None, Some("x")), None);
}
}
(* Option Basics *)
(* OCaml 99 Problems #41 *)
(* Implementation for example 41 *)
(* Tests *)
let () =
(* Add tests *)
print_endline "โ OCaml tests passed"