Learning Rust 🦀: 12 - Basic enum
Tags: learning, rust, programming, development
Published: 2023-09-02 15:58:04

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](https://github.com/FadyGrAb/rust-learning-series)  
If you don't want to install Rust locally, you can play with all the code of this series in the official [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021) 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](https://www.linkedin.com/in/fady-abdelmassih) and [Twitter](https://twitter.com/fadyGrAb).

## Table of Contents:
- [What's an enum](#whats-an-enum)
- [Defining enum](#defining-enum)
- [Structs and enums](#structs-and-enums)
- [Implementing enum methods](#implementing-enum-methods)

### What's an enum:
In the [Struct article](https://dev.to/fadygrab/learning-rust-10-the-struct-1efd), 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:
```rust
enum SimpleAnswer {
    True,
    False,
}
```
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:
```rust
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) {}
```
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:
```rust
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,
    };

}
```
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:
```rust
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!
}
```
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:
```rust
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"));

}
```

### 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:
```rust
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())
    }
}
```
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:
```sh
Finished dev [unoptimized + debuginfo] target(s) in 14.11s
     Running `target/debug/structs_basics`
Rectangle: 50
Circle: 78.5
Square: 16
```

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 👋.