Rust Enums: Attaching Data Directly to Variants
A Rust enum variant can carry its own data, acting like a mini-struct. This is perfect for modeling states with different payloads, like a `Result` that holds either a value or an error. The footgun is using a separate struct to pair an enum with.
WHY IT EXISTS Rust enums with associated data solve a common problem: how to represent a value that can be one of several different kinds, where each kind has its own unique data structure. Without this feature, you might need a struct containing both an enum for the 'kind' and other fields for the data, which is clunky and can lead to invalid states.
THE MENTAL MODEL Think of a Rust enum as a 'sum type' or a tagged union. An enum value is one of its possible variants, and only one. A struct, by contrast, is a 'product type' — it is all of its fields at once. An enum with associated data lets you say a value is either this shape of data, or that shape of data, all under a single type.
HOW IT WORKS When defining an enum, you can add parentheses after a variant's name and specify the data types it will hold. Each variant can hold different types and amounts of data. For example, enum Message { Quit, Write(String), ChangeColor(i32, i32, i32) }. The variant name, like Message::Write, acts as a constructor function. Calling Message::Write(String::from("hello")) creates an instance of the Message enum of the Write variant, containing the string "hello".
WHEN TO USE IT Use this feature whenever you need to model a type that can be in several distinct states, with each state carrying different data. This is fundamental in Rust for error handling (Result<T, E>), optional values (Option<T>), state machines, or representing heterogeneous collections like UI events (a mouse click has coordinates, a key press has a character).
WHEN NOT TO USE IT If you have a set of variants that would all hold the exact same data fields, you could use a struct. For example, a struct with a 'kind' field and a 'data' field. However, even in this case, using an enum with associated data is often more idiomatic and type-safe in Rust, as it prevents you from constructing invalid states (e.g., a V4 IP address kind with a V6 address string).
ONE CANONICAL EXAMPLE Modeling IP addresses. An address can be either IPv4 or IPv6, which have different data structures. An enum represents this perfectly: enum IpAddr { V4(u8, u8, u8, u8), V6(String) }. An IpAddr value is either a V4 variant holding four bytes, or a V6 variant holding a string. You cannot accidentally mix them up.
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.