Rust Lang

How to Use Enums in Rust

Enumerations or enums allow us to define a type and select a value from a list of possible variants within the list.

In this article, we will explore how to work with enums in the Rust programming language.

Defining an Enum

Let us consider an example where we can use an enum type. For example, if we need to store directions, we can use an enum. Typically, we can express direction in four major possible values: North East, South, and West.

If we consider the direction, a vehicle can either be moving in one of all possible directions but not more once at the same time.

This makes an enum heavily appropriate for such a structure.

To declare an enum in Rust, we start with the keyword enum, followed by the enum identifier. An example is provided below:

enum Direction {
    North,
    East,
    South,
    West
}

The previous example allows us to define a custom type of type Direction that we can reuse in the program.

You can also define an enum with a variant with no data associated with it, similar to a unit-like struct. An enum can also have variants with named data and unnamed data.

An example is as shown:

enum Person {
    Alive,
    Male(String, f64),
    Female {name: String, age: i32}
}

Enums are useful when creating a type as shown in the previous example. If we were to use struct definition, we would have to create multiple structs. Enums allow us to store all the variants under a single entity.

Enum Values

After declaring an enum, we can create an instance of an enum using the possible variants available in the type.

For example:

fn main() {
    let n = Direction::North;
    let e = Direction::East;
    let s = Direction::South;
    let w = Direction::West;
}

To access the variant of an enum, we use the double colon to separate the variant name space and its identifier.

Match Over Enums

One major use of enums is pattern matching. If you have multiple potential matches in your program, the option for an if-else block might not create a readable code. For such a scenario, we can use enums with the match operator.

The match operator allows us to compare a value against a list of patterns, such as variable names, literal values, and enum variants.

Consider the following example of the Directions enum we created earlier:

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

In the previous example, we start with the match keyword followed by the expression we wish to evaluate. Although the syntax may seem similar to an if statement, the match expression does not return a Boolean true or false. The return type can be of any type. In our case, the return type is a Direction enum.

The next block is known as a match arm. An arm contains the pattern to match and the code block to execute if the match is found.

In our example, the first arm contains the pattern of Direction::North and the => operator. This opens the block for the code to execute when the match is found. For the previous example, we print the message for the matching direction.

The compiler will evaluate the match expression and compare for a matching pattern in the provided arms. If a match is found within a specific arm, the code within the arm block is executed. Otherwise, the compiler continues with the evaluation.

We can run the previous code as follows:

rustc enums.rs
./enums

The previous code should return as follows:

$ ./enums
Direction is north

The _ Placeholder

Suppose we only want to perform specific functions for particular patterns in an enum. Then, do one common action for all others.

For such a case, we can use the _ operator, which matches all other non-specified cases within a match expression.

Let’s take the directions enum we created earlier. We can define an action if the direction is North, and if not, we run a global message for all other cases. The resulting code is as shown below:

fn main() {
    let direction:Direction = Direction::North;
    match direction {
        Direction::North => {
            println!(“Direction is north”);
        },
        _ => {
            println!(“Other Direction”)
        }
    }
}

If we change the value of “direction”, we should get the following output as:

let direction:Direction = Direction::East;
$ cargo run
Other Direction

The code inside the _ block is executed since we defined no matching case for direction East.

Zero Variant Enums

We can also create an enum with no variants. These are known as zero-variant enums, and we cannot instantiate them.

An example of zero-variant enums is provided below:

enum ZeroEnum {}

We cannot cast zero variant enums to other types.

Conclusion

In this article, we explored the basics of working with enum types in the Rust programming language. We highlighted how to work with enums in Rust, which includes the definition of an enum, the use of enum values, and how to match over enums. We hope you found this article helpful. Check the other Linux Hint articles for more tips and information.

About the author

John Otieno

My name is John and am a fellow geek like you. I am passionate about all things computers from Hardware, Operating systems to Programming. My dream is to share my knowledge with the world and help out fellow geeks. Follow my content by subscribing to LinuxHint mailing list