|
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