@@ -200,8 +200,62 @@ destructuring `let`, as we discussed previously in 'tuples.' In this case, the
200
200
## Enums
201
201
202
202
Finally, Rust has a "sum type", an * enum* . Enums are an incredibly useful
203
- feature of Rust, and are used throughout the standard library. This is an enum
204
- that is provided by the Rust standard library:
203
+ feature of Rust, and are used throughout the standard library. An ` enum ` is
204
+ a type which ties a set of alternates to a specific name. For example, below
205
+ we define ` Character ` to be either a ` Digit ` or something else. These
206
+ can be used via their fully scoped names: ` Character::Other ` (more about ` :: `
207
+ below).
208
+
209
+ ``` rust
210
+ enum Character {
211
+ Digit (i32 ),
212
+ Other ,
213
+ }
214
+ ```
215
+
216
+ An ` enum ` variant can be defined as most normal types. Below are some example
217
+ types have been listed which also would be allowed in an ` enum ` .
218
+
219
+ ``` rust
220
+ struct Empty ;
221
+ struct Color (i32 , i32 , i32 );
222
+ struct Length (i32 );
223
+ struct Status { Health : i32 , Mana : i32 , Attack : i32 , Defense : i32 }
224
+ struct HeightDatabase (Vec <i32 >);
225
+ ```
226
+
227
+ So you see that depending on the sub-datastructure, the ` enum ` variant, same as
228
+ a struct, may or may not hold data. That is, in ` Character ` , ` Digit ` is a name
229
+ tied to an ` i32 ` where ` Other ` is just a name. However, the fact that they are
230
+ distinct makes this very useful.
231
+
232
+ As with structures, enums don't by default have access to operators such as
233
+ compare ( ` == ` and ` != ` ), binary operations (` * ` and ` + ` ), and order
234
+ (` < ` and ` >= ` ). As such, using the previous ` Character ` type, the
235
+ following code is invalid:
236
+
237
+ ``` {rust,ignore}
238
+ // These assignments both succeed
239
+ let ten = Character::Digit(10);
240
+ let four = Character::Digit(4);
241
+
242
+ // Error: `*` is not implemented for type `Character`
243
+ let forty = ten * four;
244
+
245
+ // Error: `<=` is not implemented for type `Character`
246
+ let four_is_smaller = four <= ten;
247
+
248
+ // Error: `==` is not implemented for type `Character`
249
+ let four_equals_ten = four == ten;
250
+ ```
251
+
252
+ This may seem rather limiting, particularly equality being invalid; in
253
+ many cases however, it's unnecessary. Rust provides the [ ` match ` ] [ match ]
254
+ keyword, which will be examined in more detail in the next section, which
255
+ often allows better and easier branch control than a series of ` if ` /` else `
256
+ statements would. However, for our [ game] [ game ] we need the comparisons
257
+ to work so we will utilize the ` Ordering ` ` enum ` provided by the standard
258
+ library which supports such comparisons. It has this form:
205
259
206
260
``` {rust}
207
261
enum Ordering {
@@ -211,14 +265,9 @@ enum Ordering {
211
265
}
212
266
```
213
267
214
- An ` Ordering ` can only be _ one_ of ` Less ` , ` Equal ` , or ` Greater ` at any given
215
- time.
216
-
217
- Because ` Ordering ` is provided by the standard library, we can use the ` use `
218
- keyword to use it in our code. We'll learn more about ` use ` later, but it's
219
- used to bring names into scope.
220
-
221
- Here's an example of how to use ` Ordering ` :
268
+ Because we did not define ` Ordering ` , we must import it (from the std
269
+ library) with the ` use ` keyword. Here's an example of how ` Ordering ` is
270
+ used:
222
271
223
272
``` {rust}
224
273
use std::cmp::Ordering;
@@ -245,11 +294,10 @@ fn main() {
245
294
}
246
295
```
247
296
248
- There's a symbol here we haven't seen before: the double colon (` :: ` ).
249
- This is used to indicate a namespace. In this case, ` Ordering ` lives in
250
- the ` cmp ` submodule of the ` std ` module. We'll talk more about modules
251
- later in the guide. For now, all you need to know is that you can ` use `
252
- things from the standard library if you need them.
297
+ The ` :: ` symbol is used to indicate a namespace. In this case, ` Ordering ` lives
298
+ in the ` cmp ` submodule of the ` std ` module. We'll talk more about modules later
299
+ in the guide. For now, all you need to know is that you can ` use ` things from
300
+ the standard library if you need them.
253
301
254
302
Okay, let's talk about the actual code in the example. ` cmp ` is a function that
255
303
compares two things, and returns an ` Ordering ` . We return either
@@ -259,95 +307,44 @@ the two values are less, greater, or equal. Note that each variant of the
259
307
` Greater ` .
260
308
261
309
The ` ordering ` variable has the type ` Ordering ` , and so contains one of the
262
- three values. We can then do a bunch of ` if ` /` else ` comparisons to check which
263
- one it is. However, repeated ` if ` /` else ` comparisons get quite tedious. Rust
264
- has a feature that not only makes them nicer to read, but also makes sure that
265
- you never miss a case. Before we get to that, though, let's talk about another
266
- kind of enum: one with values.
310
+ three values. We then do a bunch of ` if ` /` else ` comparisons to check which
311
+ one it is.
267
312
268
- This enum has two variants, one of which has a value:
313
+ This ` Ordering::Greater ` notation is too long. Lets use ` use ` to import can
314
+ the ` enum ` variants instead. This will avoid full scoping:
269
315
270
316
``` {rust}
271
- enum OptionalInt {
272
- Value(i32),
273
- Missing,
274
- }
275
- ```
276
-
277
- This enum represents an ` i32 ` that we may or may not have. In the ` Missing `
278
- case, we have no value, but in the ` Value ` case, we do. This enum is specific
279
- to ` i32 ` s, though. We can make it usable by any type, but we haven't quite
280
- gotten there yet!
281
-
282
- You can also have any number of values in an enum:
317
+ use std::cmp::Ordering::{self, Equal, Less, Greater};
283
318
284
- ``` {rust}
285
- enum OptionalColor {
286
- Color(i32, i32, i32),
287
- Missing,
288
- }
289
- ```
290
-
291
- And you can also have something like this:
292
-
293
- ``` {rust}
294
- enum StringResult {
295
- StringOK(String),
296
- ErrorReason(String),
319
+ fn cmp(a: i32, b: i32) -> Ordering {
320
+ if a < b { Less }
321
+ else if a > b { Greater }
322
+ else { Equal }
297
323
}
298
- ```
299
- Where a ` StringResult ` is either a ` StringResult::StringOK ` , with the result of
300
- a computation, or a ` StringResult::ErrorReason ` with a ` String ` explaining
301
- what caused the computation to fail. These kinds of ` enum ` s are actually very
302
- useful and are even part of the standard library.
303
324
304
- Here is an example of using our ` StringResult ` :
325
+ fn main() {
326
+ let x = 5;
327
+ let y = 10;
305
328
306
- ``` rust
307
- enum StringResult {
308
- StringOK (String ),
309
- ErrorReason (String ),
310
- }
329
+ let ordering = cmp(x, y); // ordering: Ordering
311
330
312
- fn respond (greeting : & str ) -> StringResult {
313
- if greeting == " Hello" {
314
- StringResult :: StringOK (" Good morning!" . to_string ())
315
- } else {
316
- StringResult :: ErrorReason (" I didn't understand you!" . to_string ())
317
- }
331
+ if ordering == Less { println!("less"); }
332
+ else if ordering == Greater { println!("greater"); }
333
+ else if ordering == Equal { println!("equal"); }
318
334
}
319
335
```
320
336
321
- That's a lot of typing! We can use the ` use ` keyword to make it shorter:
337
+ Importing variants is convenient and compact, but can also cause name conflicts,
338
+ so do this with caution. It's considered good style to rarely import variants
339
+ for this reason.
322
340
323
- ``` rust
324
- use StringResult :: StringOK ;
325
- use StringResult :: ErrorReason ;
341
+ As you can see, ` enum ` s are quite a powerful tool for data representation, and are
342
+ even more useful when they're [ generic] [ generics ] across types. Before we
343
+ get to generics, though, let's talk about how to use them with pattern matching, a
344
+ tool that will let us deconstruct this sum type (the type theory term for enums)
345
+ in a very elegant way and avoid all these messy ` if ` /` else ` s.
326
346
327
- enum StringResult {
328
- StringOK (String ),
329
- ErrorReason (String ),
330
- }
331
-
332
- # fn main () {}
333
-
334
- fn respond (greeting : & str ) -> StringResult {
335
- if greeting == " Hello" {
336
- StringOK (" Good morning!" . to_string ())
337
- } else {
338
- ErrorReason (" I didn't understand you!" . to_string ())
339
- }
340
- }
341
- ```
342
347
343
- ` use ` declarations must come before anything else, which looks a little strange in this example,
344
- since we ` use ` the variants before we define them. Anyway, in the body of ` respond ` , we can just
345
- say ` StringOK ` now, rather than the full ` StringResult::StringOK ` . Importing variants can be
346
- convenient, but can also cause name conflicts, so do this with caution. It's considered good style
347
- to rarely import variants for this reason.
348
-
349
- As you can see, ` enum ` s with values are quite a powerful tool for data representation,
350
- and can be even more useful when they're generic across types. Before we get to generics,
351
- though, let's talk about how to use them with pattern matching, a tool that will
352
- let us deconstruct this sum type (the type theory term for enums) in a very elegant
353
- way and avoid all these messy ` if ` /` else ` s.
348
+ [ match ] : ./match.html
349
+ [ game ] : ./guessing-game.html#comparing-guesses
350
+ [ generics ] : ./generics.html
0 commit comments