Commit 251a9483 authored by Mathieu Nivoliez's avatar Mathieu Nivoliez

Finally, enums!!!

parent 6cbc506b
title: "Getting started with Rust: Enum on steroids!"
layout: post.liquid
is_draft: true
data:
author: Mathieu Nivoliez
img_cover: images/rust_logo.png
dsq_thread_id: ""
comments: true
lang: en
---
Hello everyone! Today subject was hard to decide on. But as the previous one was pretty tedious, I decided to go a subject more easy to speak of. So, today we are going to talk about enum in Rust!
## "Yeah,.... but what is an enum anyway?"
Let me explain this first. An enum (or enumeration) is a type where all possible value are listed.
To give an example, let say that I can take care of different dogs: doggo, doge and pupper.
Then we can define a enum to represent that:
```rust
enum DogKind {
Doggo,
Doge,
Pupper
}
```
Let's decompose that. First we use the keyword `enum` to express that we are going to define an enum. Then there is the name of the enum, in our case it is *DogKind*. Then, between `{ ... }` we define all possible value of *DogKind* separate with comas.
We are now able to use it as this:
```rust
fn take_care_of_dog(kind: DogKind) {
match kind {
DogKind::Doggo => take_care_of_doggo(),
DogKind::Doge => take_care_of_doggo(),
DogKind::Pupper => take_care_of_doggo(),
}
}
```
Do you remember in the previous posts when I said that it is a good practice to read out loud the code? Enum in Rust are a good practice for that. Let do it for training.
We declare a function `fn` called `take_care_of_dog` which take a parameter `kind` of type `DogKind`. We know that `DogKind` is an enum define before. Then we check if which value of `DogKind` the parameter `kind` matches. And for every possible match, we define an action to follow. Here the arrow can be read as *then, do that*, so the match statement can be read as *if it match Doggo, then do that*.
## "Ok, I have use other language before, this is just a switch case, no?"
Fair enough. But it is only the begining. Let me show you something really fun.
Let take again our example from before, upgraded a bit:
```rust
enum DogKind {
Doggo(String),
Doge(String, String, String),
Pupper { age: f32 }
}
```
## "What the f... ... Freacking duck?"
That usually the response if you have never used language with tagged union.
Let me explain, for each *value* of *DogKind*, we want to have specific values, specific properties.
You can use this like that:
```rust
fn say_something_about_dog(kind: DogKind) {
match kind {
DogKind::Doggo(name) => println!("Good boy, {}", name),
DogKind::Doge(such, wow, much) => println!("Such {}, wow {}, much {}", such, wow, much),
DogKind::Pupper { age } => println!("Oh this pupper is {} years old", age)
}
}
```
## "Wow, such variant... But are we oblidge to express all possible case into the match statement?"
The match is exhaustive, meaning that you have to describe *ALL* cases.
Well, there is a syntaxic element that allow you describe the "default case": the `_` placeholder.
```rust
fn say_something_about_dog(kind: DogKind) {
match kind {
DogKind::Doggo(name) => println!("Good boy, {}", name),
DogKind::Doge(such, wow, much) => println!("Such {}, wow {}, much {}", such, wow, much),
DogKind::Pupper { age } => println!("Oh this pupper is {} years old", age),
_ => println!("What is that? it's not a dog... Is it alive?"),
}
}
```
But, I cannot stress you enough that the default case is not robust. For example, let say that you add a kind of dogs: the *Doggy*. Then no match statement with the `_` will fail to compile because this new kind will fall into the default case.
Again, the `_` is not *evil*, it is a powerful too that must be use carefully.
## "What about doing the same thing for different value?"
We can do that easily with the `|`:
```rust
enum DogKind {
Doggo,
Doggy,
Doge,
Pupper
}
fn great_dog(kind: DogKind) {
match kind {
Doggo | Doggy => println!("Who is a good boy? It's you!"),
Doge => println!("Wow, such elagance, much raffinment"),
Pupper => println!("How adorable!"),
}
}
```
## "Ok, are they other things we have to know about enum and match expression?"
Yeah. First you can use brackets inside match expression:
```rust
enum DogKind {
Doggo,
Doggy,
Doge,
Pupper
}
fn great_dog(kind: DogKind) {
match kind {
Doggo | Doggy => println!("Who is a good boy? It's you!"),
Doge => println!("Wow, such elagance, much raffinment"),
Pupper => {
println!("How adorable!");
println!("I pet the pupper");
},
}
}
```
And you can use match expression as assignment.
## "Wait... what?"
Yep! Let say that you got a value that depends of the type of dog, you could write something like:
```rust
enum DogKind {
Doggo,
Doggy,
Doge,
Pupper
}
fn great_dog(kind: DogKind) {
let action = match kind {
Doggo | Doggy | Pupper => "Pet the dog",
Doge => "Pay your respect to the venerable doge"
}; // note the ";" at the end of the assignment expression.
println!("{}", action);
}
```
## "Ok, seems useful..."
Yeah it is, and you want to know something? You probably already use it.
The `Result<T,E>` and `Option<T>` are enum themself.
And they are a good example of enum with traits...
But we will see traits the next time :p
So, see you next time for more, do not hesitate to comment any error, suggestion or anything about this post.
-- Mathieu
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment