diff --git a/src/doc/trpl/compound-data-types.md b/src/doc/trpl/compound-data-types.md index afa890b84b401..754d375050bf8 100644 --- a/src/doc/trpl/compound-data-types.md +++ b/src/doc/trpl/compound-data-types.md @@ -199,154 +199,116 @@ destructuring `let`. ## Enums Finally, Rust has a "sum type", an *enum*. Enums are an incredibly useful -feature of Rust, and are used throughout the standard library. This is an enum -that is provided by the Rust standard library: +feature of Rust, and are used throughout the standard library. An `enum` is +a type which ties a set of alternates to a specific name. For example, below +we define `Character` to be either a `Digit` or something else. These +can be used via their fully scoped names: `Character::Other`. -```{rust} -enum Ordering { - Less, - Equal, - Greater, +```rust +enum Character { + Digit(i32), + Other, } ``` -An `Ordering` can only be _one_ of `Less`, `Equal`, or `Greater` at any given -time. +An `enum` variant, can be defined as most normal types. Below some example types +have been listed which also would be allowed in an `enum`. -Because `Ordering` is provided by the standard library, we can use the `use` -keyword to use it in our code. We'll learn more about `use` later, but it's -used to bring names into scope. +```rust +struct Empty; +struct Color(i32, i32, i32); +struct Length(i32); +struct Status { Health: i32, Mana: i32, Attack: i32, Defense: i32 } +struct HeightDatabase(Vec); +``` -Here's an example of how to use `Ordering`: +So you see that depending on the sub-datastructure, the `enum` variant, same as a +struct variant, may or may not hold data. That is, in `Character`, `Digit` is a name +tied to an `i32` where `Other` is just a name. However, the fact that they are distinct +makes this very useful. -```{rust} -use std::cmp::Ordering; +As with structures, enums don't by default have access to operators such as +compare ( `==` and `!=`), binary operations (`*` and `+`), and order +(`<` and `>=`). As such, using the previous `Character` type, the +following code is invalid: -fn cmp(a: i32, b: i32) -> Ordering { - if a < b { Ordering::Less } - else if a > b { Ordering::Greater } - else { Ordering::Equal } -} +```{rust, ignore} +// These assignments both succeed +let ten = Character::Digit(10); +let four = Character::Digit(4); -fn main() { - let x = 5; - let y = 10; - - let ordering = cmp(x, y); // ordering: Ordering - - if ordering == Ordering::Less { - println!("less"); - } else if ordering == Ordering::Greater { - println!("greater"); - } else if ordering == Ordering::Equal { - println!("equal"); - } -} -``` +// Error: `*` is not implemented for type `Character` +let forty = ten * four; -There's a symbol here we haven't seen before: the double colon (`::`). -This is used to indicate a namespace. In this case, `Ordering` lives in -the `cmp` submodule of the `std` module. We'll talk more about modules -later in the guide. For now, all you need to know is that you can `use` -things from the standard library if you need them. +// Error: `<` is not implemented for type `Character` +let four_is_smaller = four < ten; -Okay, let's talk about the actual code in the example. `cmp` is a function that -compares two things, and returns an `Ordering`. We return either -`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on if -the two values are greater, less, or equal. Note that each variant of the -`enum` is namespaced under the `enum` itself: it's `Ordering::Greater` not -`Greater`. +// Error: `==` is not implemented for type `Character` +let four_equals_ten = four == ten; +``` -The `ordering` variable has the type `Ordering`, and so contains one of the -three values. We can then do a bunch of `if`/`else` comparisons to check which -one it is. However, repeated `if`/`else` comparisons get quite tedious. Rust -has a feature that not only makes them nicer to read, but also makes sure that -you never miss a case. Before we get to that, though, let's talk about another -kind of enum: one with values. +This may seem rather limiting, particularly equality being invalid; in +many cases however, it's unnecessary. Rust provides the `match` keyword, +which will be examined in more detail in the next section, which allows +better and easier branch control than a series of `if`/`else` statements +would. Here, we'll briefly utilize it to avoid some complicated +alternatives. -This enum has two variants, one of which has a value: +In spite of not having equality, the match below is able to deduce the type +of the object and take the corresponding branch. It can even retrieve the +number from inside the structure. This is the typical way an `enum` is +used. -```{rust} -enum OptionalInt { - Value(i32), - Missing, +```rust +enum Character { + Digit(i32), + Other, } -``` - -This enum represents an `i32` that we may or may not have. In the `Missing` -case, we have no value, but in the `Value` case, we do. This enum is specific -to `i32`s, though. We can make it usable by any type, but we haven't quite -gotten there yet! -You can also have any number of values in an enum: +let nine = Character::Digit(9i32); +let not_a_digit = Other; -```{rust} -enum OptionalColor { - Color(i32, i32, i32), - Missing, +match nine { + Character::Digit(num) => println!("Got the digit: {:?}", num), + Character::Other => println!("Got the something else"), } -``` - -And you can also have something like this: -```{rust} -enum StringResult { - StringOK(String), - ErrorReason(String), +match not_a_digit { + Character::Digit(num) => println!("Got the digit: {:?}", num), + Character::Other => println!("Got the something else"), } ``` -Where a `StringResult` is either a `StringResult::StringOK`, with the result of -a computation, or an `StringResult::ErrorReason` with a `String` explaining -what caused the computation to fail. These kinds of `enum`s are actually very -useful and are even part of the standard library. -Here is an example of using our `StringResult`: +As this is very verbose, it can be shortened using the `use` declaration. +`use` must precede everything so we put it at the top. ```rust -enum StringResult { - StringOK(String), - ErrorReason(String), -} +use Character::Digit; +use Character::Other; -fn respond(greeting: &str) -> StringResult { - if greeting == "Hello" { - StringResult::StringOK("Good morning!".to_string()) - } else { - StringResult::ErrorReason("I didn't understand you!".to_string()) - } +enum Character { + Digit(i32), + Other, } -``` -That's a lot of typing! We can use the `use` keyword to make it shorter: +let nine = Digit(9i32); +let not_a_digit = Other; -```rust -use StringResult::StringOK; -use StringResult::ErrorReason; - -enum StringResult { - StringOK(String), - ErrorReason(String), +match nine { + Digit(num) => println!("Got the digit: {:?}", num), + Other => println!("Got the something else"), } -# fn main() {} - -fn respond(greeting: &str) -> StringResult { - if greeting == "Hello" { - StringOK("Good morning!".to_string()) - } else { - ErrorReason("I didn't understand you!".to_string()) - } +match not_a_digit { + Digit(num) => println!("Got the digit: {:?}", num), + Other => println!("Got the something else"), } ``` -`use` declarations must come before anything else, which looks a little strange in this example, -since we `use` the variants before we define them. Anyway, in the body of `respond`, we can just -say `StringOK` now, rather than the full `StringResult::StringOK`. Importing variants can be -convenient, but can also cause name conflicts, so do this with caution. It's considered good style -to rarely import variants for this reason. +Importing variants can be convenient, but can also cause name conflicts, so do this +with caution. It's considered good style to rarely import variants for this reason. As you can see, `enum`s with values are quite a powerful tool for data representation, and can be even more useful when they're generic across types. Before we get to generics, -though, let's talk about how to use them with pattern matching, a tool that will -let us deconstruct this sum type (the type theory term for enums) in a very elegant -way and avoid all these messy `if`/`else`s. +though, let's go into more detail about pattern matching which uses that `match` tool +we just saw.