diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index de7ded76280f6..b8b249fafe1c9 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -27,9 +27,9 @@ * [References and Borrowing](references-and-borrowing.md) * [Lifetimes](lifetimes.md) * [Mutability](mutability.md) + * [Structs](structs.md) * [Enums](enums.md) * [Match](match.md) - * [Structs](structs.md) * [Patterns](patterns.md) * [Method Syntax](method-syntax.md) * [Vectors](vectors.md) @@ -43,7 +43,6 @@ * [Universal Function Call Syntax](ufcs.md) * [Crates and Modules](crates-and-modules.md) * [`const` and `static`](const-and-static.md) - * [Tuple Structs](tuple-structs.md) * [Attributes](attributes.md) * [`type` aliases](type-aliases.md) * [Casting between types](casting-between-types.md) diff --git a/src/doc/trpl/enums.md b/src/doc/trpl/enums.md index 80ea25eb35ce9..443f569a3e5ab 100644 --- a/src/doc/trpl/enums.md +++ b/src/doc/trpl/enums.md @@ -1,68 +1,63 @@ % Enums -Rust has a ‘sum type’, an `enum`. Enums are an incredibly useful feature of -Rust, and are used throughout the standard library. An `enum` is a type which -relates a set of alternates to a specific name. For example, below we define -`Character` to be either a `Digit` or something else. +An `enum` in Rust is a type that represents data that could be one of +several possible variants: ```rust -enum Character { - Digit(i32), - Other, +enum Message { + Quit, + ChangeColor(i32, i32, i32), + Move { x: i32, y: i32 }, + Write(String), } ``` -Most types are allowed as the variant components of an `enum`. Here are some -examples: +Each variant can optionally have data associated with it. The syntax for +defining variants resembles the syntaxes used to define structs: you can +have variants with no data (like unit-like structs), variants with named +data, and variants with unnamed data (like tuple structs). Unlike +separate struct definitions, however, an `enum` is a single type. A +value of the enum can match any of the variants. For this reason, an +enum is sometimes called a ‘sum type’: the set of possible values of the +enum is the sum of the sets of possible values for each variant. -```rust -struct Empty; -struct Color(i32, i32, i32); -struct Length(i32); -struct Stats { Health: i32, Mana: i32, Attack: i32, Defense: i32 } -struct HeightDatabase(Vec); -``` - -You see that, depending on its type, an `enum` variant may or may not hold data. -In `Character`, for instance, `Digit` gives a meaningful name for an `i32` -value, where `Other` is only a name. However, the fact that they represent -distinct categories of `Character` is a very useful property. - -The variants of an `enum` by default are not comparable with equality operators -(`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not support other -binary operations such as `*` and `+`. As such, the following code is invalid -for the example `Character` type: - -```rust,ignore -// These assignments both succeed -let ten = Character::Digit(10); -let four = Character::Digit(4); - -// Error: `*` is not implemented for type `Character` -let forty = ten * four; +We use the `::` syntax to use the name of each variant: they’re scoped by the name +of the `enum` itself. This allows both of these to work: -// Error: `<=` is not implemented for type `Character` -let four_is_smaller = four <= ten; +```rust +# enum Message { +# Move { x: i32, y: i32 }, +# } +let x: Message = Message::Move { x: 3, y: 4 }; + +enum BoardGameTurn { + Move { squares: i32 }, + Pass, +} -// Error: `==` is not implemented for type `Character` -let four_equals_ten = four == ten; +let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 }; ``` -We use the `::` syntax to use the name of each variant: They’re scoped by the name -of the `enum` itself. This allows both of these to work: +Both variants are named `Move`, but since they’re scoped to the name of +the enum, they can both be used without conflict. + +A value of an enum type contains information about which variant it is, +in addition to any data associated with that variant. This is sometimes +referred to as a ‘tagged union’, since the data includes a ‘tag’ +indicating what type it is. The compiler uses this information to +enforce that you’re accessing the data in the enum safely. For instance, +you can’t simply try to destructure a value as if it were one of the +possible variants: ```rust,ignore -Character::Digit(10); -Hand::Digit; +fn process_color_change(msg: Message) { + let Message::ChangeColor(r, g, b) = msg; // compile-time error +} ``` -Both variants are named `Digit`, but since they’re scoped to the `enum` name, - -Not supporting these operations may seem rather limiting, but it’s a limitation -which we can overcome. There are two ways: by implementing equality ourselves, -or by pattern matching variants with [`match`][match] expressions, which you’ll -learn in the next section. We don’t know enough about Rust to implement -equality yet, but we’ll find out in the [`traits`][traits] section. +We’ll see how to safely get data out of enums when we learn about the +[`match`][match] and [`if let`][if-let] statements in the next few +chapters. [match]: match.html -[traits]: traits.html +[if-let]: if-let.html diff --git a/src/doc/trpl/match.md b/src/doc/trpl/match.md index 86b9445338966..2bb2359ba5a01 100644 --- a/src/doc/trpl/match.md +++ b/src/doc/trpl/match.md @@ -61,3 +61,40 @@ let number = match x { ``` Sometimes it’s a nice way of converting something from one type to another. + +# Matching on enums + +Another important use of the `match` keyword is to process the possible +variants of an enum: + +```rust +enum Message { + Quit, + ChangeColor(i32, i32, i32), + Move { x: i32, y: i32 }, + Write(String), +} + +fn quit() { /* ... */ } +fn change_color(r: i32, g: i32, b: i32) { /* ... */ } +fn move_cursor(x: i32, y: i32) { /* ... */ } + +fn process_message(msg: Message) { + match msg { + Message::Quit => quit(), + Message::ChangeColor(r, g, b) => change_color(r, g, b), + Message::Move { x: x, y: y } => move_cursor(x, y), + Message::Write(s) => println!("{}", s), + }; +} +``` + +Again, the Rust compiler checks exhaustiveness, so it demands that you +have a match arm for every variant of the enum. If you leave one off, it +will give you a compile-time error unless you use `_`. + +Unlike the previous uses of `match`, you can’t use the normal `if` +statement to do this. You can use the [`if let`][if-let] statement, +which can be seen as an abbreviated form of `match`. + +[if-let][if-let.html] diff --git a/src/doc/trpl/structs.md b/src/doc/trpl/structs.md index fcf928e427c25..ad7ead9319989 100644 --- a/src/doc/trpl/structs.md +++ b/src/doc/trpl/structs.md @@ -117,3 +117,82 @@ ones, and it will copy the values you don’t specify: let origin = Point3d { x: 0, y: 0, z: 0 }; let point = Point3d { z: 1, x: 2, .. origin }; ``` + +# Tuple structs + +Rust has another data type that’s like a hybrid between a [tuple][tuple] and a +struct, called a ‘tuple struct’. Tuple structs have a name, but +their fields don’t: + +```rust +struct Color(i32, i32, i32); +struct Point(i32, i32, i32); +``` + +[tuple]: primitive-types.html#tuples + +These two will not be equal, even if they have the same values: + +```rust +# struct Color(i32, i32, i32); +# struct Point(i32, i32, i32); +let black = Color(0, 0, 0); +let origin = Point(0, 0, 0); +``` + +It is almost always better to use a struct than a tuple struct. We would write +`Color` and `Point` like this instead: + +```rust +struct Color { + red: i32, + blue: i32, + green: i32, +} + +struct Point { + x: i32, + y: i32, + z: i32, +} +``` + +Now, we have actual names, rather than positions. Good names are important, +and with a struct, we have actual names. + +There _is_ one case when a tuple struct is very useful, though, and that’s a +tuple struct with only one element. We call this the ‘newtype’ pattern, because +it allows you to create a new type, distinct from that of its contained value +and expressing its own semantic meaning: + +```rust +struct Inches(i32); + +let length = Inches(10); + +let Inches(integer_length) = length; +println!("length is {} inches", integer_length); +``` + +As you can see here, you can extract the inner integer type through a +destructuring `let`, just as with regular tuples. In this case, the +`let Inches(integer_length)` assigns `10` to `integer_length`. + +# Unit-like structs + +You can define a struct with no members at all: + +```rust +struct Electron; +``` + +Such a struct is called ‘unit-like’ because it resembles the empty +tuple, `()`, sometimes called ‘unit’. Like a tuple struct, it defines a +new type. + +This is rarely useful on its own (although sometimes it can serve as a +marker type), but in combination with other features, it can become +useful. For instance, a library may ask you to create a structure that +implements a certain [trait][trait] to handle events. If you don’t have +any data you need to store in the structure, you can just create a +unit-like struct. diff --git a/src/doc/trpl/tuple-structs.md b/src/doc/trpl/tuple-structs.md deleted file mode 100644 index bdaef70711ae4..0000000000000 --- a/src/doc/trpl/tuple-structs.md +++ /dev/null @@ -1,60 +0,0 @@ -% Tuple Structs - -Rust has another data type that's like a hybrid between a [tuple][tuple] and a -[struct][struct], called a ‘tuple struct’. Tuple structs have a name, but -their fields don’t: - -```rust -struct Color(i32, i32, i32); -struct Point(i32, i32, i32); -``` - -[tuple]: primitive-types.html#tuples -[struct]: structs.html - -These two will not be equal, even if they have the same values: - -```rust -# struct Color(i32, i32, i32); -# struct Point(i32, i32, i32); -let black = Color(0, 0, 0); -let origin = Point(0, 0, 0); -``` - -It is almost always better to use a struct than a tuple struct. We would write -`Color` and `Point` like this instead: - -```rust -struct Color { - red: i32, - blue: i32, - green: i32, -} - -struct Point { - x: i32, - y: i32, - z: i32, -} -``` - -Now, we have actual names, rather than positions. Good names are important, -and with a struct, we have actual names. - -There _is_ one case when a tuple struct is very useful, though, and that’s a -tuple struct with only one element. We call this the ‘newtype’ pattern, because -it allows you to create a new type, distinct from that of its contained value -and expressing its own semantic meaning: - -```rust -struct Inches(i32); - -let length = Inches(10); - -let Inches(integer_length) = length; -println!("length is {} inches", integer_length); -``` - -As you can see here, you can extract the inner integer type through a -destructuring `let`, as we discussed previously in ‘tuples’. In this case, the -`let Inches(integer_length)` assigns `10` to `integer_length`.