Skip to content

Commit dd11c7f

Browse files
committed
Rollup merge of rust-lang#25348 - geofft:trpl-fix-enums, r=steveklabnik
The enums chapter at the moment is ... weird. The examples aren't about enums, they're about structs, and most of the chapter talks about how enums don't support comparison operators by default (which is also true of other compound data types.) I think there was a story here once, but some coherency got lost in refactoring. There are two preliminary patches here, one to combine the struct and tuple-struct chapters, and one to document unit-like structs, because enum syntax is easier to explain once you have those three. The final patch moves the enum chapter after the struct chapter, and rewrites most of it to talk about enums usefully (including covering matches on enums). r? @steveklabnik
2 parents e8c69e5 + f59f41e commit dd11c7f

File tree

5 files changed

+162
-112
lines changed

5 files changed

+162
-112
lines changed

src/doc/trpl/SUMMARY.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
* [References and Borrowing](references-and-borrowing.md)
3030
* [Lifetimes](lifetimes.md)
3131
* [Mutability](mutability.md)
32+
* [Structs](structs.md)
3233
* [Enums](enums.md)
3334
* [Match](match.md)
34-
* [Structs](structs.md)
3535
* [Patterns](patterns.md)
3636
* [Method Syntax](method-syntax.md)
3737
* [Vectors](vectors.md)
@@ -45,7 +45,6 @@
4545
* [Universal Function Call Syntax](ufcs.md)
4646
* [Crates and Modules](crates-and-modules.md)
4747
* [`const` and `static`](const-and-static.md)
48-
* [Tuple Structs](tuple-structs.md)
4948
* [Attributes](attributes.md)
5049
* [`type` aliases](type-aliases.md)
5150
* [Casting between types](casting-between-types.md)

src/doc/trpl/enums.md

+45-50
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,63 @@
11
% Enums
22

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:
75

86
```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),
1212
}
1313
```
1414

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.
1723

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:
4326

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+
}
4637

47-
// Error: `==` is not implemented for type `Character`
48-
let four_equals_ten = four == ten;
38+
let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };
4939
```
5040

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:
5351

5452
```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+
}
5756
```
5857

59-
Both variants are named `Digit`, but since they’re scoped to the `enum` name,
60-
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.
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.
6661

6762
[match]: match.html
68-
[traits]: traits.html
63+
[if-let]: if-let.html

src/doc/trpl/match.md

+37
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,40 @@ let number = match x {
6161
```
6262

6363
Sometimes it’s a nice way of converting something from one type to another.
64+
65+
# Matching on enums
66+
67+
Another important use of the `match` keyword is to process the possible
68+
variants of an enum:
69+
70+
```rust
71+
enum Message {
72+
Quit,
73+
ChangeColor(i32, i32, i32),
74+
Move { x: i32, y: i32 },
75+
Write(String),
76+
}
77+
78+
fn quit() { /* ... */ }
79+
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
80+
fn move_cursor(x: i32, y: i32) { /* ... */ }
81+
82+
fn process_message(msg: Message) {
83+
match msg {
84+
Message::Quit => quit(),
85+
Message::ChangeColor(r, g, b) => change_color(r, g, b),
86+
Message::Move { x: x, y: y } => move_cursor(x, y),
87+
Message::Write(s) => println!("{}", s),
88+
};
89+
}
90+
```
91+
92+
Again, the Rust compiler checks exhaustiveness, so it demands that you
93+
have a match arm for every variant of the enum. If you leave one off, it
94+
will give you a compile-time error unless you use `_`.
95+
96+
Unlike the previous uses of `match`, you can’t use the normal `if`
97+
statement to do this. You can use the [`if let`][if-let] statement,
98+
which can be seen as an abbreviated form of `match`.
99+
100+
[if-let][if-let.html]

src/doc/trpl/structs.md

+79
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,82 @@ ones, and it will copy the values you don’t specify:
117117
let origin = Point3d { x: 0, y: 0, z: 0 };
118118
let point = Point3d { z: 1, x: 2, .. origin };
119119
```
120+
121+
# Tuple structs
122+
123+
Rust has another data type that’s like a hybrid between a [tuple][tuple] and a
124+
struct, called a ‘tuple struct’. Tuple structs have a name, but
125+
their fields don’t:
126+
127+
```rust
128+
struct Color(i32, i32, i32);
129+
struct Point(i32, i32, i32);
130+
```
131+
132+
[tuple]: primitive-types.html#tuples
133+
134+
These two will not be equal, even if they have the same values:
135+
136+
```rust
137+
# struct Color(i32, i32, i32);
138+
# struct Point(i32, i32, i32);
139+
let black = Color(0, 0, 0);
140+
let origin = Point(0, 0, 0);
141+
```
142+
143+
It is almost always better to use a struct than a tuple struct. We would write
144+
`Color` and `Point` like this instead:
145+
146+
```rust
147+
struct Color {
148+
red: i32,
149+
blue: i32,
150+
green: i32,
151+
}
152+
153+
struct Point {
154+
x: i32,
155+
y: i32,
156+
z: i32,
157+
}
158+
```
159+
160+
Now, we have actual names, rather than positions. Good names are important,
161+
and with a struct, we have actual names.
162+
163+
There _is_ one case when a tuple struct is very useful, though, and that’s a
164+
tuple struct with only one element. We call this the ‘newtype’ pattern, because
165+
it allows you to create a new type, distinct from that of its contained value
166+
and expressing its own semantic meaning:
167+
168+
```rust
169+
struct Inches(i32);
170+
171+
let length = Inches(10);
172+
173+
let Inches(integer_length) = length;
174+
println!("length is {} inches", integer_length);
175+
```
176+
177+
As you can see here, you can extract the inner integer type through a
178+
destructuring `let`, just as with regular tuples. In this case, the
179+
`let Inches(integer_length)` assigns `10` to `integer_length`.
180+
181+
# Unit-like structs
182+
183+
You can define a struct with no members at all:
184+
185+
```rust
186+
struct Electron;
187+
```
188+
189+
Such a struct is called ‘unit-like’ because it resembles the empty
190+
tuple, `()`, sometimes called ‘unit’. Like a tuple struct, it defines a
191+
new type.
192+
193+
This is rarely useful on its own (although sometimes it can serve as a
194+
marker type), but in combination with other features, it can become
195+
useful. For instance, a library may ask you to create a structure that
196+
implements a certain [trait][trait] to handle events. If you don’t have
197+
any data you need to store in the structure, you can just create a
198+
unit-like struct.

src/doc/trpl/tuple-structs.md

-60
This file was deleted.

0 commit comments

Comments
 (0)