|
1 | 1 | % Enums |
2 | 2 |
|
3 | | -Rust has a ‘sum type’, an `enum`. Enums are an incredibly useful feature of |
4 | | -Rust, and are used throughout the standard library. An `enum` is a type which |
5 | | -relates a set of alternates to a specific name. For example, below we define |
6 | | -`Character` to be either a `Digit` or something else. |
| 3 | +An `enum` in Rust is a type that represents data that could be one of |
| 4 | +several possible variants: |
7 | 5 |
|
8 | 6 | ```rust |
9 | | -enum Character { |
10 | | - Digit(i32), |
11 | | - Other, |
| 7 | +enum Message { |
| 8 | + Quit, |
| 9 | + ChangeColor(i32, i32, i32), |
| 10 | + Move { x: i32, y: i32 }, |
| 11 | + Write(String), |
12 | 12 | } |
13 | 13 | ``` |
14 | 14 |
|
15 | | -Most types are allowed as the variant components of an `enum`. Here are some |
16 | | -examples: |
| 15 | +Each variant can optionally have data associated with it. The syntax for |
| 16 | +defining variants resembles the syntaxes used to define structs: you can |
| 17 | +have variants with no data (like unit-like structs), variants with named |
| 18 | +data, and variants with unnamed data (like tuple structs). Unlike |
| 19 | +separate struct definitions, however, an `enum` is a single type. A |
| 20 | +value of the enum can match any of the variants. For this reason, an |
| 21 | +enum is sometimes called a ‘sum type’: the set of possible values of the |
| 22 | +enum is the sum of the sets of possible values for each variant. |
17 | 23 |
|
18 | | -```rust |
19 | | -struct Empty; |
20 | | -struct Color(i32, i32, i32); |
21 | | -struct Length(i32); |
22 | | -struct Stats { Health: i32, Mana: i32, Attack: i32, Defense: i32 } |
23 | | -struct HeightDatabase(Vec<i32>); |
24 | | -``` |
25 | | - |
26 | | -You see that, depending on its type, an `enum` variant may or may not hold data. |
27 | | -In `Character`, for instance, `Digit` gives a meaningful name for an `i32` |
28 | | -value, where `Other` is only a name. However, the fact that they represent |
29 | | -distinct categories of `Character` is a very useful property. |
30 | | - |
31 | | -The variants of an `enum` by default are not comparable with equality operators |
32 | | -(`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not support other |
33 | | -binary operations such as `*` and `+`. As such, the following code is invalid |
34 | | -for the example `Character` type: |
35 | | - |
36 | | -```rust,ignore |
37 | | -// These assignments both succeed |
38 | | -let ten = Character::Digit(10); |
39 | | -let four = Character::Digit(4); |
40 | | -
|
41 | | -// Error: `*` is not implemented for type `Character` |
42 | | -let forty = ten * four; |
| 24 | +We use the `::` syntax to use the name of each variant: they’re scoped by the name |
| 25 | +of the `enum` itself. This allows both of these to work: |
43 | 26 |
|
44 | | -// Error: `<=` is not implemented for type `Character` |
45 | | -let four_is_smaller = four <= ten; |
| 27 | +```rust |
| 28 | +# enum Message { |
| 29 | +# Move { x: i32, y: i32 }, |
| 30 | +# } |
| 31 | +let x: Message = Message::Move { x: 3, y: 4 }; |
| 32 | + |
| 33 | +enum BoardGameTurn { |
| 34 | + Move { squares: i32 }, |
| 35 | + Pass, |
| 36 | +} |
46 | 37 |
|
47 | | -// Error: `==` is not implemented for type `Character` |
48 | | -let four_equals_ten = four == ten; |
| 38 | +let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 }; |
49 | 39 | ``` |
50 | 40 |
|
51 | | -We use the `::` syntax to use the name of each variant: They’re scoped by the name |
52 | | -of the `enum` itself. This allows both of these to work: |
| 41 | +Both variants are named `Move`, but since they’re scoped to the name of |
| 42 | +the enum, they can both be used without conflict. |
| 43 | + |
| 44 | +A value of an enum type contains information about which variant it is, |
| 45 | +in addition to any data associated with that variant. This is sometimes |
| 46 | +referred to as a ‘tagged union’, since the data includes a ‘tag’ |
| 47 | +indicating what type it is. The compiler uses this information to |
| 48 | +enforce that you’re accessing the data in the enum safely. For instance, |
| 49 | +you can’t simply try to destructure a value as if it were one of the |
| 50 | +possible variants: |
53 | 51 |
|
54 | 52 | ```rust,ignore |
55 | | -Character::Digit(10); |
56 | | -Hand::Digit; |
| 53 | +fn process_color_change(msg: Message) { |
| 54 | + let Message::ChangeColor(r, g, b) = msg; // compile-time error |
| 55 | +} |
57 | 56 | ``` |
58 | 57 |
|
59 | | -Both variants are named `Digit`, but since they’re scoped to the `enum` name |
60 | | -there's no ambiguity. |
61 | | - |
62 | | -Not supporting these operations may seem rather limiting, but it’s a limitation |
63 | | -which we can overcome. There are two ways: by implementing equality ourselves, |
64 | | -or by pattern matching variants with [`match`][match] expressions, which you’ll |
65 | | -learn in the next section. We don’t know enough about Rust to implement |
66 | | -equality yet, but we’ll find out in the [`traits`][traits] section. |
| 58 | +We’ll see how to safely get data out of enums when we learn about the |
| 59 | +[`match`][match] and [`if let`][if-let] statements in the next few |
| 60 | +chapters. |
67 | 61 |
|
68 | 62 | [match]: match.html |
69 | | -[traits]: traits.html |
| 63 | +[if-let]: if-let.html |
0 commit comments