tezvyn:

Rust Enums and Pattern Matching: Type-Safe Alternatives

AI-drafted, machine-checkedSource: doc.rust-lang.orgintermediate

Rust enums define a type that can be one of several variants, each holding its own data. They're used to model states like `Loading`/`Success`/`Error` or handle optional values with `Option<T>`.

WHY IT EXISTS Enums exist to eliminate bugs that arise from data without context, like using special values such as null, -1, or empty strings to represent a state. By encoding all possible states of a value into the type system itself, Rust makes invalid states unrepresentable, and the compiler can verify correctness.

THE MENTAL MODEL Think of a Rust enum as a box that can hold one of several pre-defined items, and the box is always labeled with what's inside. You might have a Message box that can hold either a Quit instruction, a Move instruction with coordinates, or a Write instruction with a string. You can't accidentally treat the Quit instruction as if it has a string, because the type system won't let you. The match keyword is how you safely check the label and handle the contents accordingly.

HOW IT WORKS An enum is defined with variants. A variant can be a simple unit (e.g., IpAddrKind::V4) or can contain data (e.g., IpAddrKind::V6(String)). To use an enum's value, you use a match expression. This expression is forced by the compiler to be exhaustive, meaning you must provide a code path, or "arm", for every possible variant. This prevents you from forgetting to handle a case. For situations where you only care about one variant and want to ignore the rest, the if let construct provides a more concise syntax.

WHEN TO USE IT Use enums whenever a value can be one of a fixed set of possibilities, especially if those possibilities carry different data. This is fundamental for modeling state machines, representing events, defining custom error types, and handling optionality. The standard library's Option<T> and Result<T, E> are the most common and powerful examples.

WHEN NOT TO USE IT Avoid enums when the set of variants is not known at compile time or needs to be extended dynamically. If you need to add new types at runtime without recompiling, a trait object (dynamic dispatch) is a more appropriate tool. Enums are for closed, compile-time known sets of variations.

ONE CANONICAL EXAMPLE The Option<T> enum, defined as enum Option<T> { Some(T), None }, is the canonical use case. It provides a type-safe way to handle values that might be absent, solving the "billion-dollar mistake" of null pointers. The compiler forces you to handle both the Some(value) and None cases, ensuring you can't use a value that isn't there.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.