Defining an Enum
Enums model data that can be one of several distinct variants. Each variant can carry different types and amounts of data.
Basic Syntax
enum IpAddrKind { V4, V6, } fn main() { let four = IpAddrKind::V4; let six = IpAddrKind::V6; route(IpAddrKind::V4); route(IpAddrKind::V6); } fn route(ip_kind: IpAddrKind) {}
Enum values are created using namespace syntax:
enum IpAddrKind { V4, V6, } fn main() { let four = IpAddrKind::V4; let six = IpAddrKind::V6; route(IpAddrKind::V4); route(IpAddrKind::V6); } fn route(ip_kind: IpAddrKind) {}
Both values have type IpAddrKind
, enabling uniform function parameters:
enum IpAddrKind { V4, V6, } fn main() { let four = IpAddrKind::V4; let six = IpAddrKind::V6; route(IpAddrKind::V4); route(IpAddrKind::V6); } fn route(ip_kind: IpAddrKind) {}
Data in Variants
Variants can carry associated data directly:
fn main() { enum IpAddr { V4(String), V6(String), } let home = IpAddr::V4(String::from("127.0.0.1")); let loopback = IpAddr::V6(String::from("::1")); }
Variant names become constructor functions: IpAddr::V4()
takes a String
and returns an IpAddr
.
Each variant can have different data types:
fn main() { enum IpAddr { V4(u8, u8, u8, u8), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1")); }
The standard library’s IpAddr
uses structs within variants:
struct Ipv4Addr { // --snip-- } struct Ipv6Addr { // --snip-- } enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), }
Complex Variants
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() {}
This enum replaces what would require multiple struct types:
struct QuitMessage; // unit struct struct MoveMessage { x: i32, y: i32, } struct WriteMessage(String); // tuple struct struct ChangeColorMessage(i32, i32, i32); // tuple struct fn main() {}
Enums enable functions accepting any variant as a single type parameter, unlike separate structs.
Methods on Enums
Enums support methods via impl
blocks:
fn main() { enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } impl Message { fn call(&self) { // method body would be defined here } } let m = Message::Write(String::from("hello")); m.call(); }
The Option<T>
Enum
Option<T>
encodes nullable values safely in the type system:
enum Option<T> { None, Some(T), }
Option<T>
is included in the prelude. Variants Some
and None
are directly accessible without the Option::
prefix.
Examples:
fn main() { let some_number = Some(5); let some_char = Some('e'); let absent_number: Option<i32> = None; }
Option vs Null Safety
Unlike TypeScript’s null
and undefined
, Rust prevents direct operations on Option<T>
values:
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
}
Compiler error:
$ cargo run
Compiling enums v0.1.0 (file:///projects/enums)
error[E0277]: cannot add `Option<i8>` to `i8`
--> src/main.rs:5:17
|
5 | let sum = x + y;
| ^ no implementation for `i8 + Option<i8>`
|
= help: the trait `Add<Option<i8>>` is not implemented for `i8`
= help: the following other types implement trait `Add<Rhs>`:
`&i8` implements `Add<i8>`
`&i8` implements `Add`
`i8` implements `Add<&i8>`
`i8` implements `Add`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `enums` (bin "enums") due to 1 previous error
This compile-time check eliminates null pointer exceptions. You must explicitly handle both Some(T)
and None
cases before accessing the inner value.
Key Benefits:
- Explicit opt-in for nullable types via
Option<T>
- Compile-time guarantee that non-
Option
types are never null - Forces explicit null checking at usage sites
To extract values from Option<T>
, use pattern matching (match
) or Option<T>
methods. Pattern matching provides exhaustive case handling and value extraction from variants.