Error Handling
map() and map_err()
map() and map_err() are adapter methods that transform the value inside an Option<T> or Result<T, E> by applying a closure to the targeted variant while passing the other variant through untouched.
How map() and map_err() Transform Variants
Option::map(f)
├── Some(v) ──> Some(f(v))
└── None ──> None
Result::map(f)
├── Ok(v) ──> Ok(f(v))
└── Err(e) ──> Err(e)
Result::map_err(g)
├── Ok(v) ──> Ok(v)
└── Err(e) ──> Err(g(e))
| Method | Receiver | Signature | Transforms | Leaves Untouched |
|---|---|---|---|---|
opt.map(f) |
Option<T> |
FnOnce(T) -> U |
Some(T) -> Some(U) |
None |
res.map(f) |
Result<T, E> |
FnOnce(T) -> U |
Ok(T) -> Ok(U) |
Err(E) |
res.map_err(g) |
Result<T, E> |
FnOnce(E) -> F |
Err(E) -> Err(F) |
Ok(T) |
opt.map_or(default, f) |
Option<T> |
U, FnOnce(T) -> U |
Some(T) -> U (unwrapped) |
None -> default |
opt.map_or_else(d, f) |
Option<T> |
FnOnce() -> U, FnOnce(T) -> U |
Some(T) -> f(v) |
None -> d() |
Transforming Some(T) and Ok(T) with map()
map() consumes the original Option<T> or Result<T, E> and applies the closure only when a value is present:
fn main() {
let raw_name: Option<&str> = Some(" ada lovelace ");
let missing_name: Option<&str> = None;
let normalized = raw_name.map(str::trim).map(str::to_uppercase);
let missing_norm = missing_name.map(str::trim).map(str::to_uppercase);
println!("normalized: {normalized:?}");
println!("missing_norm: {missing_norm:?}");
let parsed: Result<i32, _> = "21".parse::<i32>().map(|n| n * 2);
let failed: Result<i32, _> = "xx".parse::<i32>().map(|n| n * 2);
println!("parsed: {parsed:?}");
println!("failed: {failed:?}");
}
normalized: Some("ADA LOVELACE")
missing_norm: None
parsed: Ok(42)
failed: Err(ParseIntError { kind: InvalidDigit })
Transforming Err(E) with map_err()
map_err() applies a closure only to the Err(E) variant of a Result<T, E>, converting Result<T, E> into Result<T, F>:
#[derive(Debug)]
struct CliError(String);
fn parse_threads(arg: &str) -> Result<usize, CliError> {
arg.parse::<usize>()
.map(|n| n.max(1))
.map_err(|e| CliError(format!("invalid --threads value {arg:?}: {e}")))
}
fn main() {
println!("0 threads -> {:?}", parse_threads("0"));
println!("8 threads -> {:?}", parse_threads("8"));
println!("bad input -> {:?}", parse_threads("four"));
}
0 threads -> Ok(1)
8 threads -> Ok(8)
bad input -> Err(CliError("invalid --threads value \"four\": invalid digit found in string"))
Folding Into a Single Value with map_or() and map_or_else()
When you want to transform the inner value and extract it in a single step—supplying a fallback if the receiver is None or Err(e)—use map_or(default, f) (for a constant fallback) or map_or_else(fallback_fn, f) (for a lazily computed fallback):
fn main() {
let tag: Option<&str> = Some("v1.4.0");
let no_tag: Option<&str> = None;
let len_a = tag.map_or(0, str::len);
let len_b = no_tag.map_or(0, str::len);
println!("len_a = {len_a}, len_b = {len_b}");
let ok_res: Result<i32, &str> = Ok(10);
let err_res: Result<i32, &str> = Err("timeout");
let msg_ok = ok_res.map_or_else(|e| format!("ERR: {e}"), |v| format!("OK: {}", v * 10));
let msg_err = err_res.map_or_else(|e| format!("ERR: {e}"), |v| format!("OK: {}", v * 10));
println!("{msg_ok} | {msg_err}");
}
len_a = 6, len_b = 0
OK: 100 | ERR: timeout