Home Rust Tutorial: Enums in Rust Programming Language

Tutorial: Enums in Rust Programming Language

Enums in Rust are powerful constructs that allow you to define a type that can be one of several variants.

They are particularly useful for representing choices, states, or distinct types of data under one umbrella.

What You’ll Learn

1. What Are Enums?

Enums (short for enumerations) define a type by listing its possible variants. Each variant can optionally hold additional data.

Syntax

enum EnumName {
    Variant1,
    Variant2,
    Variant3,
}

2. Defining and Using Enums

Enums can have multiple variants, and you can create an instance of an enum by specifying a variant.

Example: Basic Enum

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

fn main() {
    let direction = Direction::North;

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

3. Enums with Data

Variants in an enum can hold data, which makes enums incredibly versatile.

Example: Enum with Different Types of Data

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

fn main() {
    let msg = Message::Move { x: 10, y: 20 };

    match msg {
        Message::Quit => println!("Quit message"),
        Message::Move { x, y } => println!("Move to ({}, {})", x, y),
        Message::Write(text) => println!("Write message: {}", text),
        Message::ChangeColor(r, g, b) => println!("Change color to ({}, {}, {})", r, g, b),
    }
}

4. Matching with Enums

Pattern matching using match is the most common way to work with enums.

Example: Matching Enum Variants

enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Square(f64),
}

fn area(shape: Shape) -> f64 {
    match shape {
        Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
        Shape::Rectangle(width, height) => width * height,
        Shape::Square(side) => side * side,
    }
}

fn main() {
    let circle = Shape::Circle(5.0);
    let rect = Shape::Rectangle(4.0, 6.0);
    let square = Shape::Square(3.0);

    println!("Circle area: {:.2}", area(circle));
    println!("Rectangle area: {:.2}", area(rect));
    println!("Square area: {:.2}", area(square));
}

5. Enums and Methods

You can implement methods for enums using impl.

Example: Adding Methods to Enums

enum TrafficLight {
    Red,
    Yellow,
    Green,
}

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

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

6. Option Enum

The Option enum represents an optional value and is built into Rust. It is widely used for situations where a value can be present or absent.

Definition

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

Example: Using Option

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

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

7. Result Enum

The Result enum is used for error handling. It represents either a success (Ok) or an error (Err).

Definition

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Example: Using Result

fn read_file(filename: &str) -> Result<String, String> {
    if filename == "valid.txt" {
        Ok(String::from("File content"))
    } else {
        Err(String::from("File not found"))
    }
}

fn main() {
    match read_file("invalid.txt") {
        Ok(content) => println!("File content: {}", content),
        Err(error) => println!("Error: {}", error),
    }
}

8. Practical Examples

8.1 Enum for HTTP Status Codes

enum HttpStatus {
    Ok(u16),
    NotFound(u16),
    InternalServerError(u16),
}

fn main() {
    let status = HttpStatus::Ok(200);

    match status {
        HttpStatus::Ok(code) => println!("Success with code: {}", code),
        HttpStatus::NotFound(code) => println!("Not Found with code: {}", code),
        HttpStatus::InternalServerError(code) => println!("Server error with code: {}", code),
    }
}

8.2 Enum for File Operations

enum FileOperation {
    Open(String),
    Close(String),
    Read(String, usize),
    Write(String, String),
}

fn perform_operation(op: FileOperation) {
    match op {
        FileOperation::Open(file) => println!("Opening file: {}", file),
        FileOperation::Close(file) => println!("Closing file: {}", file),
        FileOperation::Read(file, size) => println!("Reading {} bytes from file: {}", size, file),
        FileOperation::Write(file, content) => println!("Writing to file: {}\nContent: {}", file, content),
    }
}

fn main() {
    let op = FileOperation::Open(String::from("example.txt"));
    perform_operation(op);
}

8.3 Enum for Geometry

enum Geometry {
    Point(f64, f64),
    Line(f64, f64, f64, f64),
    Circle(f64, f64, f64),
}

fn describe(geo: Geometry) {
    match geo {
        Geometry::Point(x, y) => println!("Point at ({}, {})", x, y),
        Geometry::Line(x1, y1, x2, y2) => println!("Line from ({}, {}) to ({}, {})", x1, y1, x2, y2),
        Geometry::Circle(x, y, r) => println!("Circle at ({}, {}) with radius {}", x, y, r),
    }
}

fn main() {
    let point = Geometry::Point(1.0, 2.0);
    let line = Geometry::Line(0.0, 0.0, 3.0, 4.0);
    let circle = Geometry::Circle(5.0, 5.0, 10.0);

    describe(point);
    describe(line);
    describe(circle);
}

8.4 State Machine Using Enums

enum State {
    Start,
    Processing,
    Done,
}

fn transition(state: State) -> State {
    match state {
        State::Start => State::Processing,
        State::Processing => State::Done,
        State::Done => State::Start,
    }
}

fn main() {
    let mut state = State::Start;

    for _ in 0..5 {
        println!("Current state: {:?}", state);
        state = transition(state);
    }
}

9. Summary

Key Features of Enums

  • Enums group related variants into a single type.
  • Variants can hold data, making enums versatile.
  • Use pattern matching with match for exhaustive handling.
  • Rust’s built-in Option and Result enums simplify optional values and error handling.

Best Practices

  • Use enums to represent mutually exclusive states or choices.
  • Combine enums with match to write clean and safe code.
  • Add methods to enums using impl for reusable functionality.

You may also like