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:
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:
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:
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:
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:
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:
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),
};