Option<T> and Result<T, E> in Depth
with Option
Applying the ? operator to an Option<T> inside a function or closure that returns Option<U> unwraps Some(v) into v, or immediately returns None from the enclosing function when the value is None.
Desugaring of opt?
let val = expr?;
// Desugars to:
let val = match expr {
Some(v) => v,
None => return None,
};
Flattening Deep Optional Navigation Chains with ?
Without ?, traversing a sequence of optional lookups (HashMap::get, slice::first, str::strip_prefix, char::to_digit) requires either deeply nested if let Some(...) blocks or a long .and_then(...) chain. Using ? lets you write linear, top-to-bottom code:
struct Server {
tls_cert_path: Option<String>,
}
struct Cluster {
primary: Option<Server>,
}
fn primary_cert_extension(cluster: &Cluster) -> Option<&str> {
let server = cluster.primary.as_ref()?;
let path = server.tls_cert_path.as_deref()?;
let (_name, ext) = path.rsplit_once('.')?;
Some(ext)
}
fn main() {
let valid_cluster = Cluster {
primary: Some(Server {
tls_cert_path: Some(String::from("/etc/ssl/server.pem")),
}),
};
let no_tls_cluster = Cluster {
primary: Some(Server {
tls_cert_path: None,
}),
};
println!("Valid cluster ext: {:?}", primary_cert_extension(&valid_cluster));
println!("No-TLS cluster ext: {:?}", primary_cert_extension(&no_tls_cluster));
}
Valid cluster ext: Some("pem")
No-TLS cluster ext: None
Cannot Mix Option? Directly in a Result Function (E0277)
The ? operator does not implicitly convert Option<T> into Result<T, E> (or vice versa), because None carries no error information telling the compiler which E value to construct:
// ❌ does not compile
fn first_byte(s: &str) -> Result<u8, &'static str> {
let b = s.bytes().next()?; // Error: `next()` returns `Option<u8>`, not `Result`
Ok(b)
}
fn main() {}
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
--> src/main.rs:3:29
|
2 | fn first_byte(s: &str) -> Result<u8, &'static str> {
| -------------------------------------------------- this function returns a `Result`
3 | let b = s.bytes().next()?;
| ^ use `.ok_or(...)?` to provide an error compatible with `Result<u8, &str>`
To bridge between Option and Result when using ?:
- Inside a function returning
Result<T, E>, convert anOption<U>with.ok_or(err)?or.ok_or_else(|| ...)?. - Inside a function returning
Option<T>, convert aResult<U, E>with.ok()?.