โข 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
x = parse_int("42")
result = x * 2 if x is not None else None
In Rust:
let result = parse_int("42").map(|x| x * 2);
Same idea โ "apply a function if the value exists" โ but the Rust version is a single expression. Chain multiple `.map()` calls to build a transformation pipeline:
parse_int("42")
.map(|x| x * 2) // doubles: Some(84)
.map(|x| x + 1) // adds one: Some(85)
If any step encounters `None`, the whole chain short-circuits to `None`. No null checks at each step.
// map: apply a function to Some(x), pass through None
Some(4.0_f64).map(|x| x.sqrt()) // โ Some(2.0)
None::<f64>.map(|x| x.sqrt()) // โ None
// map_or: apply a function to Some(x), use default for None
Some(5_i64).map_or(0, |x| x * 2) // โ 10
None::<i64>.map_or(0, |x| x) // โ 0
// filter_map: map + flatten โ apply a function that itself returns Option
// Commonly used to extract Some values from a list of Options:
let opts = vec![Some(1), None, Some(3), None, Some(5)];
let values: Vec<i64> = opts.iter().filter_map(|x| *x).collect();
// โ [1, 3, 5]
The mental model:
| Concept | OCaml | Rust | ||
|---|---|---|---|---|
| Map over option | `Option.map f opt` | `opt.map(\ | x\ | f(x))` (method syntax) |
| Default on None | `Option.value ~default opt` | `opt.map_or(default, f)` | ||
| Map + flatten list | `List.filter_map f lst` | `iter.filter_map(f).collect()` | ||
| Chaining | `Option.map f (Option.map g opt)` | `opt.map(g).map(f)` (left-to-right) | ||
| Type annotation | Inferred from `f` | Inferred from closure return type |
// Option Map โ 99 Problems #42
// Transform Option values with map, map_or, and_then, or_else.
fn safe_sqrt(x: f64) -> Option<f64> {
if x < 0.0 { None } else { Some(x.sqrt()) }
}
fn parse_int(s: &str) -> Option<i64> {
s.trim().parse().ok()
}
/// Double an Option<i64> using map.
fn double_opt(x: Option<i64>) -> Option<i64> {
x.map(|v| v * 2)
}
/// Map over a list of Options, keeping only the Somes.
fn filter_map_demo(lst: &[Option<i64>]) -> Vec<i64> {
lst.iter().filter_map(|x| *x).collect()
}
/// Apply a function, fall back to default.
fn map_or_demo(lst: &[&str], default: i64) -> Vec<i64> {
lst.iter().map(|s| parse_int(s).map_or(default, |v| v * 10)).collect()
}
fn main() {
// map: transform Some, pass through None
println!("Some(4.0).map(sqrt) = {:?}", Some(4.0f64).map(|x| x.sqrt()));
println!("None.map(sqrt) = {:?}", None::<f64>.map(|x| x.sqrt()));
// safe_sqrt via map
println!("safe_sqrt(9.0) = {:?}", safe_sqrt(9.0));
println!("safe_sqrt(-1.0) = {:?}", safe_sqrt(-1.0));
// Chaining with map
let result = parse_int("42").map(|x| x * 2).map(|x| x + 1);
println!("parse('42')*2+1 = {:?}", result);
// map_or
println!("None.map_or(0, |x|x) = {}", None::<i64>.map_or(0, |x| x));
println!("Some(5).map_or(0,*2) = {}", Some(5i64).map_or(0, |x| x * 2));
// filter_map
let opts = vec![Some(1), None, Some(3), None, Some(5)];
println!("filter_map = {:?}", filter_map_demo(&opts));
// map_or on parse
let strs = vec!["1", "two", "3", "four"];
println!("map_or parse = {:?}", map_or_demo(&strs, -1));
// double_opt
println!("double Some(7) = {:?}", double_opt(Some(7)));
println!("double None = {:?}", double_opt(None));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map_some() {
assert_eq!(Some(5i64).map(|x| x * 2), Some(10));
}
#[test]
fn test_map_none() {
assert_eq!(None::<i64>.map(|x| x * 2), None);
}
#[test]
fn test_filter_map() {
let opts = vec![Some(1), None, Some(3)];
assert_eq!(filter_map_demo(&opts), vec![1, 3]);
}
#[test]
fn test_safe_sqrt() {
assert_eq!(safe_sqrt(4.0), Some(2.0));
assert_eq!(safe_sqrt(-1.0), None);
}
#[test]
fn test_map_or() {
assert_eq!(Some(3i64).map_or(0, |x| x * 2), 6);
assert_eq!(None::<i64>.map_or(99, |x| x), 99);
}
}
(* Option Map *)
(* OCaml 99 Problems #42 *)
(* Implementation for example 42 *)
(* Tests *)
let () =
(* Add tests *)
print_endline "โ OCaml tests passed"