Enums

Dear Computer

Chapter 10: Everything Revisited

Enums

Enums in Rust are a lot like the ones we saw in Haskell. In both languages, we use them to enumerate a fixed set of variants of a type and to optionally associate fields with each variant. A value of an enum type can only be one of the variants at a time, just like a union in C.

Definition

In Rust, we define an enum using syntax similar to Java. This enum enumerating the variants of a mathematical expression has no associated data:

Rust
enum Expression {
  Add,
  Subtract,
  Multiply,
  Divide,
  Number,
}
enum Expression {
  Add,
  Subtract,
  Multiply,
  Divide,
  Number,
}

The convention is to name the variants using UpperCamelCase rather than SCREAMING_SNAKE_CASE.

To access an enum value, we must either qualify the variant with the enum type or add a use statement. This variable assignment qualifies the variant:

Rust
let operator = Expression::Subtract;
let operator = Expression::Subtract;

This Edit enum, which models an edit in a text editor, adds associated data to each variant:

Rust
enum Edit {
  Insert(String, usize),  // new text, index
  Delete(usize, usize),   // index, length
}
enum Edit {
  Insert(String, usize),  // new text, index
  Delete(usize, usize),   // index, length
}

The fields of Insert and Delete are unnamed; each is a 2-tuple. If we prefer to name the fields, we can use the record syntax instead:

Rust
enum Edit {
  Insert { text: String, index: usize },
  Delete { index: usize, length: usize },
}
enum Edit {
  Insert { text: String, index: usize },
  Delete { index: usize, length: usize },
}

In summary, variants can have no data, unnamed data, or named data. All three variant forms may be mixed and matched within a single enum definition, as in this Color enum:

Rust
enum Color {
  Black,
  Grayscale(u8),
  RedGreenBlue { r: u8, g: u8, b: u8 },
}
enum Color {
  Black,
  Grayscale(u8),
  RedGreenBlue { r: u8, g: u8, b: u8 },
}

We saw this exact same expressiveness in Haskell's data command.

Pattern Matching

In Haskell, we used pattern matching to select a value's variant and destructuring to access a variant's associated fields. We do the same in Rust. Often this is done with a match expression, which is equivalent to Haskell's case expression. This code forms an RGB tuple from a Color value, with a case for each variant:

Rust
let color: Color = ...

let rgb = match color {
  Color::Black => (0, 0, 0),
  Color::Grayscale(gray) => (gray, gray, gray),
  Color::RedGreenBlue { r, g, b } => (r, g, b),
};
let color: Color = ...

let rgb = match color {
  Color::Black => (0, 0, 0),
  Color::Grayscale(gray) => (gray, gray, gray),
  Color::RedGreenBlue { r, g, b } => (r, g, b),
};
← StructsErrors →