Rust provides various data types to perform operations, from basic data types like strings, integers, and floating point numbers to compound data types like vectors and arrays to composite data types like structs and enumerations.

Enums (enumerations) is a data type that allows the representation of a fixed set of values, like the days of the week and the color of the rainbow. Enums are handy when the possible values for a variable are limited and known.

Defining Enums in Rust

Declaring enums in Rust is similar to declaring enums in C#. You'll use the enum keyword following the name and a set of curly braces to define enumerations. You can define the possible variants (entities) within the curly braces with a comma as the separator.

Here's an enum for the days of the week:

        enum Weekday {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
}

The Weekday enum represents the days of the week. The variants are the names of the day and have no associated values. Also, the variants of your enum can be any Rust data type.

You can access variants by specifying the variant name using the path separator (::) operator on the enum.

        let day = Weekday::Monday;

// statically typed version of the `day` variable
let day: Weekday = Weekday::Monday;

The code specifies that you want to access the Monday variant of the Weekday enum.

Variants and Discriminants of Enums

The variants of an enum are associated with integer values called discriminant. By default, the discriminant values start at zero and are incremented by 1 for subsequent variants; however, it's possible to specify custom discriminant values for each variant.

Here's an example of the Weekday enum with value-assigned variants.

        enum Weekday {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
    Sunday = 7,
}

The Weekday enum has seven variants representing each day of the week, and each day has a value (called the discriminant) assigned to them. The variants are in the order of the first to last day of the week.

You can access the discriminant value using the variant name.

        fn main() {
    println!("Monday: {}", Weekday::Monday as i32);
    println!("Tuesday: {}", Weekday::Tuesday as i32);
    println!("Wednesday: {}", Weekday::Wednesday as i32);
    println!("Thursday: {}", Weekday::Thursday as i32);
    println!("Friday: {}", Weekday::Friday as i32);
    println!("Saturday: {}", Weekday::Saturday as i32);
    println!("Sunday: {}", Weekday::Sunday as i32);
}

The println! macro is used to print the discriminants. Each println! macro invocation takes two arguments: a format string and a value. The format string specifies how to format the output; the value is the actual value that gets printed.

In this case, the format string is a string literal that contains the weekday’s name, and the value is the enum variant with an explicit cast to the i32 integer type.

The explicit cast to i32 is necessary because Rust enums are represented as integers, but the specific integer type depends on the size of the enum. By default, Rust assigns the smallest integer type that can represent all of the discriminants in the enum. But in this case, you want to print the values as i32 integers, so you need to cast them explicitly.

Here's the result from running the main function:

result from printing discriminants of an enum

Patterns Matching in Rust

Pattern matching is a Rust control structure construct useful for identifying data patterns. Pattern matching enables writing concise and efficient code while operating with advanced data structures or performing complex operations.

You'll use the match keyword followed by the => separator for the pattern and the operation for the pattern, respectively. The pattern can be any Rust expression, including literals, variables, and function calls.

Here's an example match statement:

        fn main(){
    let x = 5;
match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),

// runs if none of the patterns match
    _ => println!("something else"),
}
}

The main function matches x against several patterns and then prints the value based on the value of x. The underscore (_) pattern is a wild card pattern that's used as a catch-all for cases that aren't explicitly handled.

Enums and Pattern Matching

Enums and pattern matching are useful for expressing and working with advanced data structures in a type-safe and efficient way. You can use enums to define a fixed set of values and pattern matching to work with those values.

Here's an enum for the colors in a rainbow:

        enum Rainbow {
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet,
}

Each variant of the Rainbow enum represents a color of the rainbow. You can use pattern matching with the match statement to match patterns against the variant to control the program's flow based on the rainbow's color.

Here's a function that takes in the color variant from the Rainbow enum and prints a message based on the color.

        fn print_color(color: Rainbow) {
    match color {
        Rainbow::Red => println!("The color is red!"),
        Rainbow::Orange => println!("The color is orange!"),
        Rainbow::Yellow => println!("The color is yellow!"),
        Rainbow::Green => println!("The color is green!"),
        Rainbow::Blue => println!("The color is blue!"),
        Rainbow::Indigo => println!("The color is indigo!"),
        Rainbow::Violet => println!("The color is violet!"),
    }
}

fn main() {
   let color = Rainbow::Blue;
   print_color(color);
}
result from printing a color variant

You may get variants are never constructed or similar warnings when you'll try to execute the above code because the other variants apart from the Blue variant were never constructed in this code. Thus, in this case, you can safely ignore these warnings since it's intentional.

The print_color function takes in a color parameter of the Rainbow enum type. The match statement matches the color from a suite of colors that are accessed via the variant names. And finally, the message is printed based on the matched color.

You can use pattern matching for complex operations like multiple return values based on a condition.

        fn calculate_wavelength(color: Rainbow) -> u32 {
    match color {

        Rainbow::Red => 700,
        Rainbow::Orange => 590,
        Rainbow::Yellow => 570,
        Rainbow::Green => 510,
        Rainbow::Blue => 475,
        Rainbow::Indigo => 445,
        Rainbow::Violet => 400,
    }
}

The calculate_wavelength function takes in a color variant of the Rainbow enum type as a parameter and returns an unsigned 32-bit integer that's the wavelength of the color that matches the pattern.

Rust Also Provides Structs for Creating Custom Types

Enums are handy for defining custom types for several operations, especially in cases where the fields are known and definite.

Rust also provides structs that you can use to create custom types with named fields. Unlike enums, structs allow you to define fields of different types that you can access and modify individually.