What Result does
A type for operations that can fail — either Ok(T) or Err(E), with no exceptions ever thrown.
As an enum, Result can be exactly one of its declared variants at any time. The compiler stores a discriminant alongside the variant data, and the layout is optimised — for instance, an enum like Option<&T> uses the null-pointer niche so it fits in the same memory as a single pointer. The full canonical path is std::result::Result; bring it into scope with use std::result::Result; or refer to it by its full path.
When to use it
Use Result for every fallible operation: I/O, parsing, network, validation. Pair it with the `?` operator to propagate errors up the call stack succinctly. Use Option only when there's no useful error information to attach.
Annotated examples
Propagating errors with the ? operator
use std::fs;
use std::io;
fn read_first_line(path: &str) -> io::Result<String> {
// The ? operator is shorthand for: if Err(e), return Err(e.into());
// if Ok(v), unwrap to v. It works for both Result and Option.
let contents = fs::read_to_string(path)?;
Ok(contents.lines().next().unwrap_or("").to_string())
}
The ? operator is the canonical way to propagate errors. It also auto-converts via the From trait, so `io::Error` can become your custom error type if you implement From.
For deeper background, see the canonical Rust patterns reference for the broader context behind this section.
Mapping errors and providing context
use std::num::ParseIntError;
#[derive(Debug)]
struct ConfigError(String);
fn parse_setting(raw: &str) -> Result<i32, ConfigError> {
raw.parse::<i32>()
// map_err converts the inner error type without touching Ok
.map_err(|e: ParseIntError| ConfigError(format!("bad int '{raw}': {e}")))
}
map_err lets you wrap or transform a Result's error variant without unpacking the value — essential for adding context as errors travel up the stack.
Common pitfalls
Don't reach for `.unwrap()` or `.expect()` to satisfy the compiler — every call site is a potential panic. The `?` operator handles 95% of cases. For richer ergonomics, libraries like `anyhow` (for applications) and `thiserror` (for libraries) add structured context without losing the type-safety of Result.
For deeper background, see an in-depth Rust idioms cheat sheet for the broader context behind this section.
Performance & threading notes
The size of an enum is the size of its largest variant plus a discriminant tag (often optimised away by niche analysis). Pattern matching compiles to a single jump table; matching is essentially free at runtime.