Skip to content

Option and Result

One line: Rust has no null and no exceptions — it has two ordinary enums, Option for a value that may be absent and Result for an operation that may fail, and a compiler that will not let you read either one without saying what happens in the other case.

Neither type is built into the language. Both are enums anyone could have written, which is worth knowing early: everything in this section is a method on a normal type, and you can read the source. What the language contributes is match — because it must account for every variant, "I forgot the empty case" stops being a class of bug and becomes a build error.

The bulk of the section is the middle ground between match and .unwrap(). There are a dozen small methods that each answer one question — what if it is missing, what if I want to transform it, what if building the fallback is expensive — and choosing among them is most of what fluency in Rust looks like from the outside.

Those methods have a shape, and it is worth seeing once before you start — nothing below assumes you already know it. Every box is a type, every label is a method that moves you between types, and the two blue boxes in the middle are the pair this section is about:

Directed graph of the conversions between Option and Result. T, E and the never type sit across the top; Result<T, E> and Option<T> in the middle; arrows labelled with method names - ok, err, ok_or[_else], unwrap, expect, unwrap_or[_else], unwrap_or_default, unwrap_err, expect_err, map, and[_then], map_err, map_or[_else], or[_else], filter, xor, replace - connect them to each other and to Result<T, F>, Result<U, E>, U and Option<U> along the bottom.

Figure 1-1, "Option and Result transformations", from Effective Rust ↗ by David Drysdale, © 2024 Galloglass Consulting Limited. Reproduced unmodified under CC BY-NC-ND 4.0 ↗; original drawing ↗.

Red marks the four that can panic. Notice where they land: unwrap and expect reach T through the same box as unwrap_or[_else] and unwrap_or_default, which reach that identical T without ever aborting. Same destination, different answer on the sad arm — and choosing between those two behaviours is most of what the pages below are teaching.

Lesson Level What it teaches
Some and None 101 The enum itself: two shapes, one exhaustive match, and why Some(0) is not None
Some is a constructor, not a flag 101 → 201 Why Some(None) is E0308 and not "present but empty" — Some is a fn(T) -> Option<T>, so the argument must be the payload; plus the one type where Some(None) is the right answer
Option vs Result 101 Absence versus failure — and the single question ("could the caller ask why not?") that decides which type you want
What a monad is 301 The shape Option, Result and Vec all share — and why Rust uses monads without ever saying the word
if let 101 A match with one arm — the family (if let / let … else / while let / matches!), and the exhaustiveness you trade away
while let 201 The loop whose exit condition is a pattern — and the one bug if let cannot have: a body that never makes progress
One arm, many values 101 → 201 8 \| 12 \| 18 and 0..=7: the two ways to widen a match arm, the two lints that catch a botched collapse, and the one mistake neither can see
unwrap_or 201 The default you already have — eager, consuming, and it erases the very difference it stood in for
unwrap_or_else 201 The lazy fallback — built only if needed, allowed to consume what it captures, and on a Result the only one handed the error
unwrap_or_default 201 The fallback the type chose — a derived Default is the type's zero, not your domain's, and a missing impl is a guard rail
map_or and map_or_else 201 Transform and fall back in one call — the default written first and run last, and the clippy lint on each side of it
Transforms instead of match 201 The whole vocabulary at once: which sentence picks map, and_then, ok_or or map_err — plus the _else suffix measured, and the as_ref() that E0507 is asking for
The ? operator 201 Unwrap, return, and convert the error through From — three statements in one character, and the third is the one people are surprised by
expect 201 The message is a claim about why this cannot fail — and being unable to write it is the finding
What a panic costs 201 The other half of unwrap: where the panic points, what unwinding gives back (memory) and what it does not (your work), and why the exit code is 101
Reading a backtrace 201 The caller the panic message will not name: switching the frame list on, reading it innermost-first, and the two functions --release inlines out of it
Partial functions 201 Why Option exists at all: it turns a function that is undefined somewhere into one that always answers
Returning None on error 201 Why input.parse().ok() is usually a downgrade: four distinct causes arriving as one indistinguishable None
Zero wins is not zero games 201 A guard on the wrong condition: the input with no answer is the one that gets a number, and Result does not stop it
Initial values 201 The job where Option is usually the wrong tool — Rust lets you declare without initializing and proves you assigned
Optional function arguments 201 No default parameters, no overloading — the five shapes that replace them, and why Option<&T> beats &Option<T>
Option fields 101 Option in a type definition: required-by-default fields, and when Option<Vec<T>> is right
Option is a one-item collection 201 It iterates; None really is variant 0; and why the tag is often free
Nullable pointers 201 Rust has no null, so an absent pointer is a different type — Option<Box<T>>, free, and what makes a recursive type possible
The Result you are reading is probably an alias 201 io::Result<T> is Result<T, io::Error> — how to expand an alias and read what can actually go wrong, plus Ok(()) and Infallible
Shadowing and unwrap 201 Two unrelated ideas the tutorials tie together: what keeps the original Option alive is Copy, not the shadow — and what shadowing is really for
Six kinds of zero 201 Where Option runs out: six reasons a cell is empty, one enum, and a report the compiler audits
  • Enums — the feature both of these types are made of
  • Errors — what a Result does on its way out of a program somebody else runs
  • Ownership — why unwrap on a String consumes it and on an i32 does not

OPTION.md is the full reading order, and lists the eight jobs the standard library says Option exists to do.

Po polsku

W Ruscie nie ma null i nie ma wyjątków. Są za to dwa zwyczajne wyliczenia (enums): Option (w polskim Tour of Rust: Opcja) na wartość, której może nie być, i Result (Rezultat) na operację, która może się nie udać. Warto od razu wiedzieć, że żaden z tych typów nie jest wbudowany w język — to zwykłe wyliczenia, które ktokolwiek mógłby napisać, a każdą metodę z tego działu można po prostu przeczytać w źródłach std. Język dokłada do nich jedną rzecz: match, który musi obsłużyć wszystkie warianty, więc „zapomniałem o przypadku pustym” przestaje być klasą błędów, a staje się błędem kompilacji.

Czytelnikowi przychodzącemu z Javy, C# albo Pythona najbardziej przydadzą się dwa rozróżnienia. Option to nie jest null z ładniejszą nazwą: nie da się go przez pomyłkę użyć jak wartości — najpierw trzeba go otworzyć, a kompilator tego pilnuje. Result to nie jest try/catch: błąd wędruje jako zwracana wartość, więc sygnatura funkcji mówi wprost, co może pójść nie tak, i nic nie przeskakuje nad kodem wywołującym. Polska literatura mówi tu o obsłudze błędów, ale bez mechanizmu wyjątków — cała „obsługa” to zwykłe dopasowanie wzorców do zwróconej wartości.

Reszta działu sprowadza się do jednego pytania: co zrobić w przestrzeni między match a .unwrap(). Diagram powyżej czyta się prosto — każde pudełko to typ, każda etykieta to metoda przenosząca cię między typami, a na czerwono zaznaczone są te cztery, które mogą spanikować. Zwróć uwagę, gdzie lądują: unwrap i expect docierają do dokładnie tego samego T, co unwrap_or[_else] i unwrap_or_default, tylko te drugie robią to, nie przerywając programu. Ten sam cel, inna odpowiedź na smutnej gałęzi — i wybór między tymi dwoma zachowaniami to jest właśnie to, czego uczą kolejne strony z tabeli.

Szukaj po polsku: obsługa błędów w Ruscie · wyliczenia Opcja i Rezultat · rust Option vs Result · rust error handling without exceptions · rust unwrap_or_else