Rust Study Session #4
Ch. 6: Enums and Pattern Matching
2020.08.28 - Salvatore La Bua
Summary
- Defining an enum
- Encoding meaning into the data.
- The match control flow operator
- Different behaviour for each value.
- Concise control flow with if let
- An alternative match syntax.
Defining an Enum
What are enums
- Enumerations allow the definition of custom types by enumerating all the possible variants.
- In some ways, enums are similar to structs.
Custom data type: IP version
Enum definition
| enum IpAddrKind { |
| V4, |
| V6, |
| } |
Instances
| let four = IpAddrKind::V4; |
| let six = IpAddrKind::V6; |
Custom data type: IP version
Function definition
| fn route(ip_kind: IpAddrKind) {} |
Function call
| route(IpAddrKind::V4); |
| route(IpAddrKind::V6); |
Custom data type: IP version
Implementation using structs
| enum IpAddrKind { |
| V4, |
| V6, |
| } |
| |
| struct IpAddr { |
| kind: IpAddrKind, |
| address: String, |
| } |
| |
| let home = IpAddr { |
| kind: IpAddrKind::V4, |
| address: String::from("127.0.0.1"), |
| }; |
| |
| let loopback = IpAddr { |
| kind: IpAddrKind::V6, |
| address: String::from("::1"), |
| }; |
Custom data type: IP version
Implementation using enums
| enum IpAddr { |
| V4(String), |
| V6(String), |
| } |
| |
| let home = IpAddr::V4(String::from("127.0.0.1")); |
| |
| let loopback = IpAddr::V6(String::from("::1")); |
Similarly,
| enum IpAddr { |
| V4(u8, u8, u8, u8), |
| V6(String), |
| } |
| |
| let home = IpAddr::V4(127, 0, 0, 1); |
| |
| let loopback = IpAddr::V6(String::from("::1")); |
Rust built-in IpAddr type implementation
| struct Ipv4Addr { |
| |
| } |
| |
| struct Ipv6Addr { |
| |
| } |
| |
| enum IpAddr { |
| V4(Ipv4Addr), |
| V6(Ipv6Addr), |
| } |
※ Built-in IpAddr documentation
Custom data type: Message
Definition
| enum Message { |
| Quit, |
| Move { x: i32, y: i32 }, |
| Write(String), |
| ChangeColor(i32, i32, i32), |
| } |
Similarly,
| struct QuitMessage; |
| struct MoveMessage { |
| x: i32, |
| y: i32, |
| } |
| struct WriteMessage(String); |
| struct ChangeColorMessage(i32, i32, i32); |
Custom data type: Message
Defining methods
| impl Message { |
| fn call(&self) { |
| |
| } |
| } |
| |
| let m = Message::Write(String::from("hello")); |
| m.call(); |
The Option enum
- Sometimes it is necessary to specify that a data type may or may not contain a value.
- The Null value, which is commonly available in other programming languages, is purposedly missing in Rust.
- The Option enum replaces the use of Null in Rust.
The Option enum
Built-in definition
| enum Option<T> { |
| Some(T), |
| None, |
| } |
※ Option documentation
- Automatically included in the prelude.
- The Option:: namespace is not needed.
The Option enum
Instances
| let some_number = Some(5); |
| let some_string = Some("a string"); |
| |
| let absent_number: Option<i32> = None; |
Variants
- Some
- Data type is automatically inferred.
- None
- It is necessary to specify the data type.
The Option enum
An example
| let x: i8 = 5; |
| let y: Option<i8> = Some(5); |
| |
| let sum = x + y; |
※ This code does not compile
- Option<i8> and i8 are not of the same type.
The match operator
Coins example
| enum Coin { |
| Penny, |
| Nickel, |
| Dime, |
| Quarter, |
| } |
| |
| fn value_in_cents(coin: Coin) -> u8 { |
| match coin { |
| Coin::Penny => 1, |
| Coin::Nickel => 5, |
| Coin::Dime => 10, |
| Coin::Quarter => 25, |
| } |
| } |
Coins example
A different implementation
| fn value_in_cents(coin: Coin) -> u8 { |
| match coin { |
| Coin::Penny => { |
| println!("Lucky penny!"); |
| 1 |
| } |
| Coin::Nickel => 5, |
| Coin::Dime => 10, |
| Coin::Quarter => 25, |
| } |
| } |
Coins example
Pattern-to-value binding
| #[derive(Debug)] |
| enum UsState { |
| Alabama, |
| Alaska, |
| |
| } |
| |
| enum Coin { |
| Penny, |
| Nickel, |
| Dime, |
| Quarter(UsState), |
| } |
Coins example
Pattern-to-value binding
| fn value_in_cents(coin: Coin) -> u8 { |
| match coin { |
| Coin::Penny => 1, |
| Coin::Nickel => 5, |
| Coin::Dime => 10, |
| Coin::Quarter(state) => { |
| println!("State quarter from {:?}!", state); |
| 25 |
| } |
| } |
| } |
Matching with Option<T>
Matching and binding
| fn plus_one(x: Option<i32>) -> Option<i32> { |
| match x { |
| None => None, |
| Some(i) => Some(i + 1), |
| } |
| } |
| |
| let five = Some(5); |
| let six = plus_one(five); |
| let none = plus_one(None); |
Matches are exhaustive
| fn plus_one(x: Option<i32>) -> Option<i32> { |
| match x { |
| Some(i) => Some(i + 1), |
| } |
| } |
※ This code does not compile
- The None branch is missing.
The _ placeholder
| let some_u8_value = 0u8; |
| match some_u8_value { |
| 1 => println!("one"), |
| 3 => println!("three"), |
| 5 => println!("five"), |
| 7 => println!("seven"), |
| _ => (), |
| } |
Concise Control Flow with if let
Match example
| let some_u8_value = Some(0u8); |
| match some_u8_value { |
| Some(3) => println!("three"), |
| _ => (), |
| } |
Or using if let,
| if let Some(3) = some_u8_value { |
| println!("three"); |
| } |
※ Cleaner code, but exhaustive check no longer enforced
Coins example
| let mut count = 0; |
| match coin { |
| Coin::Quarter(state) => println!("State quarter from {:?}!", state), |
| _ => count += 1, |
| } |
Or using if let,
| let mut count = 0; |
| if let Coin::Quarter(state) = coin { |
| println!("State quarter from {:?}!", state); |
| } else { |
| count += 1; |
| } |
□
References
From The Rust Programming Language book:
- Ch. 06: Enums and Pattern Matching [EN] [JP]
- Ch. 18: Patterns and Matching [EN] [JP]
Appendices:
- IpAddr documentation: [EN]
- Option documentation: [EN]
Rust Study Session #4 Ch. 6: Enums and Pattern Matching 2020.08.28 - Salvatore La Bua
{"metaMigratedAt":"2023-06-15T12:02:48.588Z","metaMigratedFrom":"YAML","title":"Rust Ch.6 - Enums and Pattern Matching","breaks":false,"description":"Rust Ch.6 - Enums and Pattern Matching","slideOptions":"{\"theme\":\"black\",\"slideNumber\":\"c/t\",\"center\":false,\"transition\":\"fade\",\"keyboard\":true}","contributors":"[{\"id\":\"c5f0d40d-be35-4660-a8b4-7736feeb9327\",\"add\":11202,\"del\":1654}]"}