Option<T> and Result<T, E> in Depth
ok_or() and ok_or_else()
The .ok_or(err) and .ok_or_else(err_fn) methods convert an Option<T> into a Result<T, E>, mapping Some(v) to Ok(v) and mapping None to Err(err) so you can propagate missing values with the ? operator inside functions that return Result.
Signatures and Evaluation Strategy
impl<T> Option<T> {
pub fn ok_or<E>(self, err: E) -> Result<T, E>;
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E>;
}
| Method | Evaluation of Error Value | Best Used When... |
|---|---|---|
opt.ok_or(err) |
Eager (err is constructed before calling ok_or, even if opt is Some) |
err is a cheap constant, unit enum variant (MyError::NotFound), or &'static str |
opt.ok_or_else(|| ...) |
Lazy (closure runs only when opt is None) |
Constructing the error allocates (format!(...), String::from(...), Box::new(...)) or calls a function |
res.ok() (Inverse on Result<T, E>) |
Discards Err(e) |
Converts Result<T, E> -> Option<T> (Ok(v) -> Some(v), Err(_) -> None) |
Option<T> Result<T, E>
├── Some(v) ── .ok_or_else(f) ──> Ok(v) (f is NOT called)
└── None ── .ok_or_else(f) ──> Err(f()) (f is called lazily)
Bridging Option Lookups into Result Functions with ?
When a function returns Result<T, AppError> and calls a method that returns Option<U> (such as slice.first(), map.get(), or str::split_once()), chain .ok_or(...) or .ok_or_else(...) before ?:
use std::collections::HashMap;
#[derive(Debug)]
enum ConfigError {
MissingHeader,
MissingKey(String),
}
fn extract_host(raw_line: &str, env: &HashMap<&str, &str>) -> Result<String, ConfigError> {
// 1. Cheap unit enum variant -> use eager `.ok_or()`:
let (_prefix, key) = raw_line
.split_once('=')
.ok_or(ConfigError::MissingHeader)?;
// 2. Error allocates a `String` -> use lazy `.ok_or_else()`:
let host = env
.get(key)
.ok_or_else(|| ConfigError::MissingKey(key.to_owned()))?;
Ok((*host).to_owned())
}
fn main() {
let env = HashMap::from([("DB_HOST", "10.0.0.5")]);
println!("Valid: {:?}", extract_host("target=DB_HOST", &env));
println!("Bad fmt: {:?}", extract_host("invalid_line", &env));
println!("Missing: {:?}", extract_host("target=REDIS_HOST", &env));
}
Valid: Ok("10.0.0.5")
Bad fmt: Err(MissingHeader)
Missing: Err(MissingKey("REDIS_HOST"))
(Note: Clippy's or_fun_call lint warns automatically if you pass a function call or format!(...) expression to eager .ok_or(...) instead of .ok_or_else(|| ...).)