DEV Community

Cover image for Learning Rust πŸ¦€: 12 - Basic enum
Fady GA 😎
Fady GA 😎

Posted on

Learning Rust πŸ¦€: 12 - Basic enum

This is the first article in about 3 or 4 articles that will discuss enum and pattern matching. In this one, I'll show you how to define and use a basic enum.

⚠️ Remember!

You can find all the code snippets for this series in its accompanying repo

If you don't want to install Rust locally, you can play with all the code of this series in the official Rust Playground that can be found on its official page.

⚠️⚠️ The articles in this series are loosely following the contents of "The Rust Programming Language, 2nd Edition" by Steve Klabnik and Carol Nichols in a way that reflects my understanding from a Python developer's perspective.

⭐ I try to publish a new article every week (maybe more if the Rust gods πŸ™Œ are generous 😁) so stay tuned πŸ˜‰. I'll be posting "new articles updates" on my LinkedIn and Twitter.

Table of Contents:

What's an enum:

In the Struct article, we saw how a Struct can be used to group relevant data together as in the width and height of a Rectangle. The enum is similar to the struct in a way that it "enumerates" all the "variants" of the custom type we are creating in one place. Think of our earlier Rectangle struct, with enum, we can make a Shape type that contains a Rectangle, a Circle, and a Square. Each with its own "properties" and "functionality". To me, I couldn't find a similar concept in Python so this is a new one for me.

Defining enum:

We use the enum keyword to define enums. The definition can be as simple as the following:

enum SimpleAnswer {
    True,
    False,
}
Enter fullscreen mode Exit fullscreen mode

Here we are defining a SimpleAnswer custom type and its variants could be either True or False (needed for an automatic exam grading system for example).

We can very easily use them and pass them to functions as follows:

enum SimpleAnswer {
    True,
    False,
}

fn main() {
    // Enum values
    let correct_answer = SimpleAnswer::True;
    let wrong_answer = SimpleAnswer::False;

    // Enums can be passed to functions
    evaluate_answer(correct_answer);
    evaluate_answer(wrong_answer);
}

fn evaluate_answer(user_answer: SimpleAnswer) {}
Enter fullscreen mode Exit fullscreen mode

Here we are creating two answers that use our SimpleAnswer type, one is correct and the other is wrong. And we can pass any of them to the evaluate_answer function for evaluation (that does nothing for now😁).

Struct and enums:

Right now, our enum doesn't contain any data! Just the variants names. One could be tempted to use his Struct knowledge and write something like this to include data, say the value of the answer:

enum SimpleAnswer {
    True,
    False,
}

struct UserAnswer {
    value: String,
    evaluation: SimpleAnswer,
}

fn main() {
    let true_with_value = UserAnswer {
        value: String::from("The sky is blue"),
        evaluation: SimpleAnswer::True,
    };

    let false_with_value = UserAnswer {
        value: String::from("The sky is brown"),
        evaluation: SimpleAnswer::False,
    };

}
Enter fullscreen mode Exit fullscreen mode

This could work but it's too much typing 😁 also there is a much simpler way to include data in enums. We can define an enum with as many variants as we want and with as many data bound to those variants as we want, take the follow as an example:

enum Superhero {
    Batman, // No data, he is ... Batman
    Superman(u32), // Current altitude as u32
    WonderWoman { lasso_of_truth: bool, sword: bool }, // Struct-like data
    Flash(u32, u32, u32), // Speedforce alarm levels as u32
    GreenLantern(LanternCorps), // LanternCorps is a Struct!
}
Enter fullscreen mode Exit fullscreen mode

Each variant can take its data in a different way, this is cool 😎! So, getting back to our "Answers" enum example, we could integrate the answer value as follows:

enum Answer {
    True(String),
    False(String),
}

fn main() {

    // Enums with values
    let true_with_value = Answer::True(String::from("The sky is blue"));
    let false_with_value = Answer::False(String::from("The sky is brown"));

}
Enter fullscreen mode Exit fullscreen mode

Implementing enum methods:

Similar to structs, we can bind enums with methods with the impl keyword and use the &self shorthand to pass the enum to the methods. For demonstrating this one, take a look at the following example implementing a Shape type similar to what I've mentioned at the beginning of this article:

enum Shape {
    Rectangle { width: f32, height: f32 },
    Circle(f32),
    Square(f32),
}

impl Shape {
    fn area(&self) -> f32 {
        match self {
            self::Shape::Rectangle {
                width: w,
                height: h,
            } => w * h,
            self::Shape::Circle(radius) => radius * radius * 3.14,
            self::Shape::Square(side) => side * side,
        }
    }

    fn get_type(&self) -> &str {
        match self {
            self::Shape::Rectangle {
                width: _,
                height: _,
            } => "Rectangle",
            self::Shape::Circle(_) => "Circle",
            self::Shape::Square(_) => "Square",
        }
    }
}

fn main() {
    let r = Shape::Rectangle {
        width: 10.0,
        height: 5.0,
    };
    let c = Shape::Circle(5.0);
    let s = Shape::Square(4.0);

    let shapes = [r, c, s];

    for shape in shapes.iter() {
        println!("{}: {}", shape.get_type(), shape.area())
    }
}
Enter fullscreen mode Exit fullscreen mode

There is a lot going on in this example so let's break it down πŸ˜‰. First, we defined our Shape enum that has 3 variants, Rectangle, Circle, and Square. For the Rectangle, it has a struct-like data structure that accepts the width and height properties as floats (f32). For the Circle and Square, they only take one float value which is the radius and the side for each shape respectively.

Then we have implemented two methond bound to this Shape enum, the area and get_type. They use something called Pattern Matching with the match keyword that we will discuss in detail in the next article. For now, just know that area calculates the area of the shape and get_type returns the shape type as a string literal.

Lastly, we have created 3 variables, r, c, and s which are Rectangle, Circle, and Square variants of the Shape type respectively. Then we crammed all of them in an array called shapes and we looped over this array printing the type and the area of each shape as follows:

Finished dev [unoptimized + debuginfo] target(s) in 14.11s
     Running `target/debug/structs_basics`
Rectangle: 50
Circle: 78.5
Square: 16
Enter fullscreen mode Exit fullscreen mode

That's it for now for enums. In the next one, we will dig deeper into the Pattern Matching that we glimpsed in this article. See you then πŸ‘‹.

Top comments (0)