Enums

I'm not gonna explain the general concept of enums, since you should already know that if you have a bit of programming experience. Rusts enums are special in the sense that they represent algebraic data types, like they exist in F#, OCaml, or Haskell.

This basically means that the variants of an enum can contain individual data fields, applying the same rules as for a struct, the only difference being that you are forced to check for a variant before using its fields. This makes enums extremely flexible and opens up of a couple of interesting possibilities.

For example, you can replace polymorphism with an enum in many cases, if you want to. You have to define all variants locally, so there is way more coupling, but the data is saved in-place under the hood, instead of requiring a reference to somewhere else. Since there is less indirection and more data locality in memory, this can improve performance. You also don't need a common interface or trait for the variants, so you gain additional flexibility, but more on that later.

Another example is a multi-typed argument. You can have some data available in different formats and instead of having a function for each type, you can make a function that accepts both types and handles them inside. The possibilities are nearly endless and we will revisit enums a fair amount of times in this article.

Of course, we can't conclude the chapter without showing some example code, so let's take a look at how the example that was just given can be implemented:

fn deserialize_binary(binary_data: Vec<u8>) -> Save { println!("{}", binary_data[0]); Save }
fn parse_text(text_data: String) -> Save { println!("{}", text_data); Save }

// Just for the function signature
struct Save;

enum SaveData {
  Binary(Vec<u8>),
  PlainText(String),
}

// Generates the enum
// Note that enum variants aren't independent types
fn generate_default_save() -> SaveData {
  SaveData::PlainText(
    String::from("BEGIN DATA\nEND DATA")
  )
}

// Basically just wraps handling for different types
// in a single function
fn load_save(save_data: SaveData) -> Save {
  match save_data {
    SaveData::Binary(binary_data) => {
      deserialize_binary(binary_data)
    }
    SaveData::PlainText(text_data) => {
      parse_text(text_data)
    }
  }
}