Error Handling
The Ok and Err Variants
Ok(T) and Err(E) are the two variants of the standard library's Result<T, E> enum. Because both variants are automatically imported into every file by Rust's prelude, you write Ok(value) and Err(error) directly without the Result:: prefix.
Symmetry and Type Roles of Ok(T) and Err(E)
Ok(T): Wraps the success value of typeT.Err(E): Wraps the failure diagnostic value of typeE(which can be any type—an error enum, aString,&'static str, or an integer code).
Every Ok(v) or Err(e) expression constructs a full Result<T, E> value. If a single expression (such as let r = Ok(10);) only mentions Ok(10) without constraining E (or only mentions Err("bad") without constraining T), the compiler requires a type annotation so both T and E are known (E0282):
fn main() {
// Both `T` (`u32`) and `E` (`&str`) are specified in the annotation:
let success: Result<u32, &str> = Ok(200);
let failure: Result<u32, &str> = Err("connection refused");
println!("success = {success:?}, failure = {failure:?}");
}
success = Ok(200), failure = Err("connection refused")
Extracting and Transforming Ok and Err
| Goal | Idiomatic Method / Syntax | Behavior |
|---|---|---|
| Pattern match both cases | match res { Ok(v) => ..., Err(e) => ... } |
Exhaustively handles both Ok and Err |
Unwrap Ok(v) or return early |
let Ok(v) = res else { return ...; }; or res? |
Extracts v into scope or exits on Err |
Transform Ok(v) while leaving Err(e) untouched |
res.map(|v| v * 2) |
Applies closure only to Ok(v) |
Transform Err(e) while leaving Ok(v) untouched |
res.map_err(|e| format!("failed: {e}")) |
Applies closure only to Err(e) |
Borrow inner payload without moving res |
res.as_ref() / res.as_mut() |
Converts &Result<T, E> into Result<&T, &E> |
fn parse_and_scale(raw: &str, factor: i32) -> Result<i32, String> {
raw.trim()
.parse::<i32>()
.map(|n| n * factor) // Runs ONLY if `.parse()` returned `Ok(n)`
.map_err(|e| format!("could not parse '{raw}': {e}")) // Runs ONLY if `.parse()` returned `Err(e)`
}
fn main() {
println!("{:?}", parse_and_scale(" 15 ", 3));
println!("{:?}", parse_and_scale("xyz", 3));
}
Ok(45)
Err("could not parse 'xyz': invalid digit found in string")
Passing Ok as a Constructor Function to .map(Ok)
Because Ok and Err are 1-element tuple variants, Ok is also a first-class function fn(T) -> Result<T, E> (and Err is fn(E) -> Result<T, E>). You can pass Ok directly to Option::map or Iterator::map:
fn main() {
let numbers = [1, 2, 3];
let wrapped: [Result<i32, &str>; 3] = numbers.map(Ok);
println!("Wrapped in Ok: {wrapped:?}");
}
Wrapped in Ok: [Ok(1), Ok(2), Ok(3)]