Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More TRPl editing #24571

Merged
merged 13 commits into from
Apr 21, 2015
8 changes: 4 additions & 4 deletions src/doc/trpl/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* [Concurrency](concurrency.md)
* [Error Handling](error-handling.md)
* [FFI](ffi.md)
* [Deref coercions](deref-coercions.md)
* [Syntax and Semantics](syntax-and-semantics.md)
* [Variable Bindings](variable-bindings.md)
* [Functions](functions.md)
Expand All @@ -30,15 +29,15 @@
* [Move semantics](move-semantics.md)
* [Enums](enums.md)
* [Match](match.md)
* [Patterns](patterns.md)
* [Structs](structs.md)
* [Patterns](patterns.md)
* [Method Syntax](method-syntax.md)
* [Drop](drop.md)
* [Vectors](vectors.md)
* [Strings](strings.md)
* [Generics](generics.md)
* [Traits](traits.md)
* [Operators and Overloading](operators-and-overloading.md)
* [Generics](generics.md)
* [Drop](drop.md)
* [if let](if-let.md)
* [Trait Objects](trait-objects.md)
* [Closures](closures.md)
Expand All @@ -53,6 +52,7 @@
* [Casting between types](casting-between-types.md)
* [Associated Types](associated-types.md)
* [Unsized Types](unsized-types.md)
* [Deref coercions](deref-coercions.md)
* [Macros](macros.md)
* [`unsafe` Code](unsafe-code.md)
* [Nightly Rust](nightly-rust.md)
Expand Down
129 changes: 25 additions & 104 deletions src/doc/trpl/enums.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
% Enums

Finally, 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. These
can be used via their fully scoped names: `Character::Other` (more about `::`
below).
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.

```rust
enum Character {
Expand All @@ -14,14 +12,14 @@ enum Character {
}
```

Most normal types are allowed as the variant components of an `enum`. Here are
some examples:
Most types are allowed as the variant components of an `enum`. Here are some
examples:

```rust
struct Empty;
struct Color(i32, i32, i32);
struct Length(i32);
struct Status { Health: i32, Mana: i32, Attack: i32, Defense: i32 }
struct Stats { Health: i32, Mana: i32, Attack: i32, Defense: i32 }
struct HeightDatabase(Vec<i32>);
```

Expand All @@ -30,12 +28,12 @@ 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.

As with structures, 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:
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}
```rust,ignore
// These assignments both succeed
let ten = Character::Digit(10);
let four = Character::Digit(4);
Expand All @@ -50,98 +48,21 @@ let four_is_smaller = four <= ten;
let four_equals_ten = four == ten;
```

This 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
chapter. We don't know enough about Rust to implement equality yet, but we can
use the `Ordering` enum from the standard library, which does:
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:

```
enum Ordering {
Less,
Equal,
Greater,
}
```

Because `Ordering` has already been defined for us, we will import it with the
`use` keyword. Here's an example of how it is used:

```{rust}
use std::cmp::Ordering;

fn cmp(a: i32, b: i32) -> Ordering {
if a < b { Ordering::Less }
else if a > b { Ordering::Greater }
else { Ordering::Equal }
}

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");
}
}
```

The `::` symbol 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.

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
whether the first value is less than, greater than, or equal to the second. Note
that each variant of the `enum` is namespaced under the `enum` itself: it's
`Ordering::Greater`, not `Greater`.

The `ordering` variable has the type `Ordering`, and so contains one of the
three values. We then do a bunch of `if`/`else` comparisons to check which
one it is.

This `Ordering::Greater` notation is too long. Let's use another form of `use`
to import the `enum` variants instead. This will avoid full scoping:

```{rust}
use std::cmp::Ordering::{self, Equal, Less, Greater};

fn cmp(a: i32, b: i32) -> Ordering {
if a < b { Less }
else if a > b { Greater }
else { Equal }
}

fn main() {
let x = 5;
let y = 10;

let ordering = cmp(x, y); // ordering: Ordering

if ordering == Less { println!("less"); }
else if ordering == Greater { println!("greater"); }
else if ordering == Equal { println!("equal"); }
}
```rust,ignore
Character::Digit(10);
Hand::Digit;
```

Importing variants is convenient and compact, but can also cause name conflicts,
so do this with caution. For this reason, it's normally considered better style
to `use` an enum rather than its variants directly.
Both variants are named `Digit`, but since they’re scoped to the `enum` name,

As you can see, `enum`s are quite a powerful tool for data representation, and
are even more useful when they're [generic][generics] across types. Before we
get to generics, though, let's talk about how to use enums with pattern
matching, a tool that will let us deconstruct sum types (the type theory term
for enums) like `Ordering` in a very elegant way that avoids all these messy
and brittle `if`/`else`s.
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.

[match]: ./match.html
[generics]: ./generics.html
[match]: match.html
[traits]: traits.html
Loading