Enums¶
One line: An enum names a closed set of alternatives and makes that set a type — and because a match on it has to account for every alternative, the compiler can tell you which code a new alternative breaks.
Rust's enum is not C's. A C enum is an integer with names attached; Rust's is a sum type — each variant may carry its own data, of its own shape — which is why Option, Result, and most well-designed Rust data models are enums and nothing about them is built into the language.
Two things follow, and the section is mostly about the second. An enum lets you model "exactly one of these" so that the impossible combinations cannot be written down. And match turns adding a variant into a build error everywhere the new case matters, which converts a discipline you have to remember into a list the compiler computes.
| Lesson | Level | What it teaches |
|---|---|---|
| What an enum is | 101 → 201 | The declaration, the four shapes of variant, behaviour in an impl block, and the E0004 you get for free when a variant is added — plus why a fieldless enum casts to an integer and a payload-carrying one does not |
| Variants that carry data | 201 | Product versus sum, counted rather than asserted; what the payload costs, measured — the niche that makes Option<Box<T>> the same eight bytes as the pointer, and the rare 104-byte variant every 4-byte one pays for until it is boxed |
| An enum instead of a bool | 201 | Two bool parameters accept the backwards call and two enums do not — plus the newtype that also refuses it without naming either state, and the two clippy lints that stay quiet under four bools |
| A typo becomes a binding | 201 | Under use Enum::*, a mistyped arm is a catch-all variable, not an error — zero warnings, and the two lints that look like they should catch it do not |
| An enum as a state machine | 201 | match (state, event): the compiler enumerates the whole transition table, names the cells you left out, and stops the moment you write _ |
Enum pages that live elsewhere¶
Option and Result are ordinary enums, and they are met on the first day rather than in a section about enums — so their lessons stay where a beginner will find them. This section links them rather than moving them:
SomeandNone— the two-variant enum in the preludeOptionvsResult— absence versus failure- Six kinds of zero — writing your own when two variants are not enough
if letandwhile let— matching one variant, and giving up exhaustiveness to do it- One arm, many values —
A | Band ranges Optionis a one-item collection — the discriminant, and when the tag is free- Nullable pointers —
Option<Box<T>>, and recursive types - What a union is — the untagged version, and the desync only it can have
- Generic enums —
<T>on an enum, and the two parametersResult<T, E>is made of - A generic recursive type — the
Boxa type that contains itself needs, and why a hand-rolledNext/Endenum isOptionwith the API removed
OPTION.md is the full reading order for Option itself.
Not yet written¶
The rest of the map, listed here rather than as empty pages so the gaps are visible: enums with a lifetime parameter (enum Token<'a>, which is the other thing those brackets hold), #[non_exhaustive] and what it does to a downstream crate's match, explicit discriminants and repr (enum Code { Ok = 200 }, repr(u8), and converting an integer back with TryFrom), custom error enums with Display, Error and From (the thiserror shape, by hand first), implementing Display for an enum — the match inside fmt, which is the usual first hand-written trait impl — and match guards and binding @.
Po polsku¶
Polska nazwa jest tu pierwszą pułapką. „Typ wyliczeniowy” zna każdy z C, Pascala albo Javy i znaczy tam dokładnie tyle, ile mówi: wyliczenie nazw, czyli garść stałych całkowitych z ładniejszymi etykietami. Rustowe wyliczenie (enum) to co innego — typ sumaryczny (sum type), czyli unia z etykietą, w której każdy wariant może nieść własne dane, własnego kształtu. Dlatego Option, Result i Cow nie są wbudowane w język, tylko są zwyczajnymi wyliczeniami z biblioteki standardowej, i dlatego pierwszy odruch przyniesiony z C — „to i tak w gruncie rzeczy liczba” — trzeba tutaj odłożyć na bok.
Druga rzecz, i o niej jest większość tego rozdziału: match na wyliczeniu musi obsłużyć każdy wariant, więc dorzucenie wariantu pół roku później zamienia się w błąd kompilacji (E0004) w każdym miejscu, które ma od tej chwili dziurę. To zamienia dyscyplinę, o której trzeba pamiętać, w listę, którą wylicza kompilator — odpowiednik grepa po całym projekcie, tyle że kompletny i nie do pominięcia. Cztery lekcje z poziomu 201 są w istocie czterema sposobami, na jakie tę gwarancję da się stracić: wygodnym _, literówką pod use Enum::*, dwoma argumentami typu bool zamiast dwóch wyliczeń i wreszcie _ w tablicy przejść automatu. Zdanie do zapamiętania brzmi więc: wyczerpujące dopasowanie nie jest tym, co daje wyliczenie, tylko tym, z czego match może zrezygnować.
Na koniec uwaga nawigacyjna, bo układ rozdziału bywa mylący: lekcji o Option i Result tu nie ma, choć to najzwyklejsze wyliczenia z dwoma wariantami — mieszkają w 17_Option_and_Result, bo spotyka się je pierwszego dnia, a nie przy okazji teorii typów. Polskie tłumaczenie Tour of Rust mówi o „Opcji” i „Rezultacie”, ale w kodzie, w komunikatach kompilatora i w wyszukiwarce zostają angielskie — tak samo jak enum, match i derive.
Szukaj po polsku: typ wyliczeniowy · typ sumaryczny · dopasowanie wyczerpujące · rust enum sum type · rust E0004 non-exhaustive patterns