Rust Enums

What Are Enums in Rust?

Enums, short for “enumerations,” are custom types that can represent multiple related values, each as a variant. They are particularly useful for scenarios where a value can only be one of a predefined set of possibilities.

Defining an Enum

You define an enum using the enum keyword followed by the name and its variants.

Syntax:

enum EnumName {
Variant1,
Variant2,
Variant3,
}

Example:

enum Direction {
North,
East,
South,
West,
}

fn main() {
let direction = Direction::North;
match direction {
Direction::North => println!("Heading North"),
Direction::East => println!("Heading East"),
Direction::South => println!("Heading South"),
Direction::West => println!("Heading West"),
}
}

Output:

Heading North

Features of Rust Enums

  1. Multiple Variants: Each enum can have several variants.
  2. Associated Data: Variants can carry additional data, making enums versatile.
  3. Pattern Matching: Enums work seamlessly with Rust’s powerful pattern matching.
  4. Type Safety: Enums ensure that only valid values are used.

Enums with Associated Data

Variants in an enum can include additional data, allowing you to store extra information.

Example:

enum Message {
Text(String),
Image { url: String, size: u32 },
Quit,
}

fn main() {
let msg = Message::Image {
url: String::from("https://example.com/image.png"),
size: 1024,
};

match msg {
Message::Text(content) => println!("Text message: {}", content),
Message::Image { url, size } => println!("Image URL: {}, Size: {}", url, size),
Message::Quit => println!("Quit message received."),
}
}

Output:

Image URL: https://example.com/image.png, Size: 1024

Enums with Methods

You can define methods for enums using the impl block. This makes enums more versatile and encapsulated.

Example:

enum TrafficLight {
Red,
Yellow,
Green,
}

impl TrafficLight {
fn time(&self) -> u32 {
match self {
TrafficLight::Red => 30,
TrafficLight::Yellow => 5,
TrafficLight::Green => 60,
}
}
}

fn main() {
let light = TrafficLight::Green;
println!("Green light duration: {} seconds", light.time());
}

Output:

Green light duration: 60 seconds

Using Enums with Pattern Matching

Pattern matching is a core feature of Rust that works seamlessly with enums. It allows you to handle different enum variants efficiently.

Example:

enum Result {
Success(String),
Error(i32),
}

fn main() {
let operation = Result::Success(String::from("File saved successfully"));

match operation {
Result::Success(msg) => println!("Operation succeeded: {}", msg),
Result::Error(code) => println!("Operation failed with error code: {}", code),
}
}

Output:

Operation succeeded: File saved successfully

Enums and Option Type

The Option enum is a built-in example in Rust and is widely used to represent optional values.

Definition:

enum Option<T> {
Some(T),
None,
}

Example:

fn divide(a: i32, b: i32) -> Option<f64> {
if b == 0 {
None
} else {
Some(a as f64 / b as f64)
}
}

fn main() {
match divide(10, 2) {
Some(result) => println!("Result: {}", result),
None => println!("Cannot divide by zero"),
}
}

Output:

Result: 5

Common Use Cases of Enums

  1. Representing States: Enums are great for representing states in a program, such as Loading, Error, or Success.
  2. Handling Variants: Use enums to handle multiple related types in a structured way.
  3. Error Handling: Enums like Result make error handling safe and robust.

Advanced Enums: Recursive Enums

Enums can refer to themselves, making them suitable for complex structures like linked lists or trees.

Example:

enum List {
Node(i32, Box<List>),
Nil,
}

fn main() {
let list = List::Node(1, Box::new(List::Node(2, Box::new(List::Nil))));
// Represents: 1 -> 2 -> Nil
}

Enums vs Structs

FeatureEnumStruct
VariantsMultiple variants in a single typeFixed fields
Data FlexibilityEach variant can have different data typesFields have defined types
Use CaseRepresent choices or statesGroup related data into one entity

Leave a Comment

BoxofLearn