# Enums

Enums are custom types, defined like this:

enum IpAddrKind {
    V4,
    V6,
}

Using enums:

let four = IpAddrKind::V4;

fn route(ip_kind: IpAddrKind) {}

Enums may have some data attached to them:

enum IpAddr {
  V4(u8, u8, u8, u8),
  V6(String)
}

let home = IpAddr::V4(127,0,0,0);
let loopback = IpAddr::V6(String::from("::1"));

or

enum Message {
  Quit,
  Move { x: i32, y: i32 },
  Write(String),
  ChangeColor(i32, i32, i32),
}

In this case the enum members become constructors. It's an alternative to creating a struct, e.g.:

struct QuitMessage; // unit struct
struct MoveMessage {
  x: i32,
  y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct

# Enum Methods

Similarly to structs, enums can have methods:

impl Message {
  fn call(&self) {
    // method body would be defined here
  }
}

let m = Message::Write(String::from("hello"));
m.call();

# Option Enum

Similarly to some .NET libraries (like Optional (opens new window)) Rust provides an Option enum to be used instead of null.

Null

Rust doesn not have NULL! It uses Option<T> monad instead.

let some_number = Some(5); //same as Option<i32>::Some(5)
let some_string = Some("a string");

let absent_number: Option<i32> = None; // same as Option<i32>::None

Prelude

Option<T>, None, and Some are available "automatically" in every file.

Option provides us with explicit information that a variable might not have any actual value. In some other languages variable a of type String could be some string or null. In Rust, a variable of type String has to have some string (even empty one)! It's another example of how Rust focuses on safety.

# Match

match is quite useful with enums (like Option<T>):

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);

An example of handling errors:

let f = File::open("hello.txt");

let f = match f {
  Ok(file) => file,
  Err(error) => match error.kind() {
    ErrorKind::NotFound => match File::create("hello.txt") {
      Ok(fc) => fc,
      Err(e) => panic!("Problem creating the file: {:?}", e),
    },
    other_error => {
      panic!("Problem opening the file: {:?}", other_error)
    }
  },
};

TIP

Alternatively, we could use unwrap_or_else() method on Result.

# If let

if let is a syntax sugar for match. It is useful when we're interested only in one of match patterns:

let config_max = Some(3u8);
if let Some(max) = config_max {
  println!("The maximum is configured to be {}", max);
}

Some(max) is a pattern, while config_max is a value being matched.

Last Updated: 1/15/2023, 6:32:34 PM