Featured

What Are Enums in Rust?

Enums are a powerful feature in Rust for defining types that can represent one of several possible variants.

 

In this blog post, we’ll explore why enums are useful, show you how to define and enhance them with implementation blocks, and then consider adding data to enum variants to create even more flexible and expressive types.

 

Why Use Enums?

Consider a situation where you must define a mutable variable to store day-of-the-week information. You could simply create a variable and assign it the desired day as a string, as follows:

 

let mut day = "Monday".to_string();

 

While this approach works, it’s far from ideal. The set of weekdays is limited, and you don’t want to allow random strings. Additionally, there’s a risk of misspelling a day name, leading to errors.

 

One potential solution might be to declare a vector of strings, with each string representing a day of the week, as shown in this listing.

 

let week_day = vec![

   "Monday".to_string(),

   "Tuesday".to_string(),

   "Wednesday".to_string(),

   "Thursday".to_string(),

   "Friday".to_string(),

   "Saturday".to_string(),

   "Sunday".to_string(),

];

day = week_day[1]

 

You could then set the day variable to a specific day using an index. However, this approach has its own issues. For instance, attempting to move a value out of a vector, will result in an error. This is because, unlike structs, vectors do not support the partial move of elements. Although you could use the clone function to work around this issue, there are remaining unnecessary complexity, such as requiring you to memorize index values and their corresponding days.

 

Defining Enums

A much better solution is to use enums to define a type by enumerating all its possible variants, as shown in this listing, through the enum keyword.

 

enum WeekDay {

   Monday,

   Tuesday,

   Wednesday,

   Thursday,

   Friday,

   Saturday,

   Sunday,

}

 

To define an enum, use the enum keyword, followed by a name for the enum. In our example, the days are called the variants of the enum. Enums are somewhat similar to structs. A key difference, however, is that structs associate a type with each field, while enums do not define a type for their variants. Instead, each variant is simply a member of the enum.

 

In the main function, now create a variable and set it as equal to the desired day variant:

 

fn main() {

   let day = WeekDay::Saturday;

   let day = WeekDay::Satrday; // Error

}

 

To create an instance of the enum, we must specify one of its variants using the double colon syntax (::). Using the enum, misspelling the day of the week cannot happen now. For instance, the second line of code will throw an error. Additionally, with enums, you don’t need to remember the indexes that correspond to each day.

 

Implementation Blocks for Enums

Let’s consider a scenario where a company has organized an event with many participants. Some participants traveled by car, some by train, and others by airplane. The company plans to reimburse travel expenses based on the mode of transportation. For this task, the company first must determine the participant’s travel type and then calculate the allowance based on that type.

 

We can model this situation using an enum called TravelType, with three variants of Car, Train, and Airplane, as shown.

 

enum TravelType {

   Car,

   Train,

   Airplane,

}

 

You can group functionality related to an enum using impl blocks, just like with structs. Let’s add a method called travel_allowance to the TravelType enum. The updated code is shown in this listing.

 

impl TravelType {

   fn travel_allowance(&self, miles: f32) -> f32 {

      let allowance = match self {

         TravelType::Car => miles * 2.0,

         TravelType::Train => miles * 3.0,

         TravelType::Airplane => miles * 5.0,

      };

      allowance

   }

}

 

This method takes a reference to self and the number of miles covered as parameters. The output is the computed allowance based on the travel type, which is comprehensively checked inside the match arms. We can now use the enum and the variant in the main function, as shown in this listing.

 

fn main() {

   let my_trip = TravelType::Car;

   let allowance = my_trip.travel_allowance(60.0);

   println!("Travel allowance: ${}", allowance);

}

 

Running this code will output the travel allowance based on the distance traveled. In this case, the result will be 120.

 

Adding Data to Enum Variants

Enums in Rust are powerful and can have data associated with them. Instead of passing the number of miles as a separate argument, for instance, you can associate this data directly with the enum variants, as shown here.

 

enum TravelType {

   Car(f32),

   Train(f32),

   Airplane(f32),

}

 

Now, when declaring an instance of TravelType, you can specify the miles as associated data held by the variant in the following way:

 

fn main() {

   let my_trip = TravelType::Car(60.0);

}

 

You also need to update the travel_allowance method to work with this new type of enum. The updated code is shown in this listing.

 

impl TravelType {

   fn travel_allowance(&self) -> f32 { // parameter miles: f32 is

           // no longer required

      let allowance = match self {

         TravelType::Car(miles) => miles * 2.0,

         TravelType::Train(miles) => miles * 3.0,

         TravelType::Airplane(miles) => miles * 5.0,

      }

      allowance

   }

}

 

You don’t need to explicitly provide the miles information to the travel_allowance method. The miles information is already contained by the enum variants passed into the method. Notice that, in the match arms, we mentioned a variable in parentheses, which holds the associated data with the variants.

 

In main, we can now call the method with no inputs, as shown in this listing.

 

fn main() {

   let my_trip = TravelType::Car(60.0);

   let allowance = my_trip.travel_allowance(); // no inputs

   println!("Travel allowance: ${}", allowance);

}

 

Editor’s note: This post has been adapted from a section of the book Rust: The Practical Guide by Nouman Azam. Dr. Azam is an associate professor of computer science at the National University of Computer and Emerging Sciences. He also teaches online programming courses about Rust and MATLAB, and reaches a community of more than 50,000 students. 

 

This post was originally published 5/2025.

Recommendation

Rust: The Practical Guide
Rust: The Practical Guide

Get “close to the machine” by programming with Rust! Discover how to effectively use this low-level language to create fast and efficient code. Set up Rust, compile your first program, and learn the language essentials: variables, functions, conditionals, and more. Walk through Rust’s unique ownership model and modular system, and then move on to more complex features, from flexibility and abstraction to web programming and text processing. Numerous code examples and exercises make this your go-to practical resource for Rust!

Learn More
Rheinwerk Computing
by Rheinwerk Computing

Rheinwerk Computing is an imprint of Rheinwerk Publishing and publishes books by leading experts in the fields of programming, administration, security, analytics, and more.

Comments