Enums and Pattern Matching in Rust

Introduction

Welcome again to your journey into Rust programming. In our previous lesson, we delved into structs, a vital building block for organizing related data in Rust. Today, we'll shift our focus to enums and pattern matching, essential concepts that will enhance your ability to handle diverse data types.

In this lesson, you will learn what enums are, why they are used, and how to apply pattern matching to manage enum data types effectively. We'll also explore how to implement methods on enums, similar to structs. By the lesson's end, you'll confidently use enums and pattern matching to process messages and handle the presence or absence of values, a crucial skill in Rust programming.

Defining Enums in Rust

Enums in Rust allow you to define a type that can be one of several variants. Let's see how you can define an enum:

enum Message {
    Quit,                      // A simple variant with no data
    Move { x: i32, y: i32 },   // A struct-like variant containing named fields for coordinates
    Write(String),             // A tuple variant containing a single String value
}

Here, the Message enum has three variants: Quit, Move with associated integer values x and y, and Write, which stores a String. This structure allows you to encapsulate varying types of data under a single data type.

An excellent practical example of enums in the Rust standard library is the Option<T> enum, which is used to express the possibility of absence of a value. The Option enum is defined in the standard library as:

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

In other words, the Option enum has two variants:

  • None, representing the absence of a value;
  • Some(T), representing the presence of a value of type T. T is called a generic type, and we'll be delving into those in the next unit!

Using Option allows Rust to handle nullable scenarios in a type-safe way, eliminating many potential runtime errors.

Creating and Using Enum Variants

Now, let's see how to create instances of these variants:

fn main() {
    let msg1 = Message::Move { x: 10, y: 20 };
    let msg2 = Message::Write(String::from("Hello, Rust!"));
    
    let some_number = Some(5);
    let some_string = Some("a string");
    let absent_number: Option<i32> = None;
}

In this simple snippet:

  • msg1 is an instance of the Move variant with x set to 10 and y set to 20.
  • msg2 is an instance of the Write variant, created using the String::from function to convert a string literal to a String object.
  • some_number is an instance of Option<i32> with the variant Some containing the value 5.
  • some_string is an instance of Option<&str> with the variant Some containing the string "a string".
  • absent_number is an instance of Option<i32> with the variant None, representing the absence of a value.

These instances let you represent concrete actions or messages in your application, enabling you to react appropriately in your code.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal