|
2 | 2 |
|
3 | 3 | Rust has a ‘sum type’, an `enum`. Enums are an incredibly useful feature of
|
4 | 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. |
| 5 | +relates a set of alternates to a specific name. Each variant of an |
| 6 | +`enum` is defined like a struct, and can hold any data (or no data, like |
| 7 | +a unit-like struct). |
7 | 8 |
|
8 | 9 | ```rust
|
9 |
| -enum Character { |
10 |
| - Digit(i32), |
11 |
| - Other, |
| 10 | +enum Message { |
| 11 | + Quit, |
| 12 | + ChangeColor(i32, i32, i32), |
| 13 | + Move { x: i32, y: i32 }, |
| 14 | + Write(String), |
12 | 15 | }
|
13 | 16 | ```
|
14 | 17 |
|
15 |
| -Most types are allowed as the variant components of an `enum`. Here are some |
16 |
| -examples: |
| 18 | +We use the `::` syntax to use the name of each variant: they’re scoped by the name |
| 19 | +of the `enum` itself. This allows both of these to work: |
17 | 20 |
|
18 | 21 | ```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: |
| 22 | +# enum Message { |
| 23 | +# Move { x: i32, y: i32 }, |
| 24 | +# } |
| 25 | +let x = Message::Move { x: 3, y: 4 }; |
35 | 26 |
|
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; |
43 |
| -
|
44 |
| -// Error: `<=` is not implemented for type `Character` |
45 |
| -let four_is_smaller = four <= ten; |
| 27 | +enum BoardGame { |
| 28 | + Move { squares: i32 }, |
| 29 | + Pass, |
| 30 | +} |
46 | 31 |
|
47 |
| -// Error: `==` is not implemented for type `Character` |
48 |
| -let four_equals_ten = four == ten; |
| 32 | +let y = BoardGame::Move { squares: 1 }; |
49 | 33 | ```
|
50 | 34 |
|
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: |
| 35 | +Both variants are named `Move`, but since they’re scoped to the name of |
| 36 | +the enum, they can both be used without conflict. |
| 37 | + |
| 38 | +Rust’s enums are similar to C’s enums, but they can also hold data. In |
| 39 | +this respect, they are also somewhat similar to C unions. Unlike C |
| 40 | +unions, however, Rust enums carry information about which variant a |
| 41 | +particular instance of the union is. (This is sometimes referred to as a |
| 42 | +‘tagged union’ in other languages.) The compiler uses this information |
| 43 | +to enforce that you’re accessing the data in the enum safely: |
53 | 44 |
|
54 | 45 | ```rust,ignore
|
55 |
| -Character::Digit(10); |
56 |
| -Hand::Digit; |
| 46 | +fn process_color_change(msg: Message) { |
| 47 | + let Message::ChangeColor(r, g, b) = msg; // compile-time error |
| 48 | +} |
57 | 49 | ```
|
58 | 50 |
|
59 |
| -Both variants are named `Digit`, but since they’re scoped to the `enum` name, |
| 51 | +You can use [matches][match] to process all possible variants of a union (or |
| 52 | +the [`if let`][if-let] statement, which we’ll see later, to match on a |
| 53 | +single one): |
60 | 54 |
|
61 |
| -Not supporting these operations may seem rather limiting, but it’s a limitation |
62 |
| -which we can overcome. There are two ways: by implementing equality ourselves, |
63 |
| -or by pattern matching variants with [`match`][match] expressions, which you’ll |
64 |
| -learn in the next section. We don’t know enough about Rust to implement |
65 |
| -equality yet, but we’ll find out in the [`traits`][traits] section. |
| 55 | +```rust |
| 56 | +# enum Message { |
| 57 | +# Quit, |
| 58 | +# ChangeColor(i32, i32, i32), |
| 59 | +# Move { x: i32, y: i32 }, |
| 60 | +# Write(String), |
| 61 | +# } |
| 62 | +fn quit() { /* ... */ } |
| 63 | +fn change_color(r: i32, g: i32, b: i32) { /* ... */ } |
| 64 | +fn move_cursor(x: i32, y: i32) { /* ... */ } |
| 65 | + |
| 66 | +fn process_message(msg: Message) { |
| 67 | + match msg { |
| 68 | + Message::Quit => quit(), |
| 69 | + Message::ChangeColor(r, g, b) => change_color(r, g, b), |
| 70 | + Message::Move { x: x, y: y } => move_cursor(x, y), |
| 71 | + Message::Write(s) => println!("{}", s), |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
66 | 75 |
|
67 | 76 | [match]: match.html
|
68 |
| -[traits]: traits.html |
| 77 | +[if-let]: if-let.html |
0 commit comments