Copyedit section 4 of tutorial

This commit is contained in:
Tim Chevalier 2012-10-10 20:08:08 -07:00
parent 6627ac6623
commit d7b8512eae
1 changed files with 49 additions and 42 deletions

View File

@ -545,8 +545,8 @@ You can define your own syntax extensions with the macro system. For details, se
## Conditionals
We've seen `if` pass by a few times already. To recap, braces are
compulsory, an optional `else` clause can be appended, and multiple
We've seen `if` expressions a few times already. To recap, braces are
compulsory, an `if` can have an optional `else` clause, and multiple
`if`/`else` constructs can be chained together:
~~~~
@ -559,10 +559,10 @@ if false {
}
~~~~
The condition given to an `if` construct *must* be of type boolean (no
implicit conversion happens). If the arms return a value, this value
must be of the same type for every arm in which control reaches the
end of the block:
The condition given to an `if` construct *must* be of type `bool` (no
implicit conversion happens). If the arms are blocks that have a
value, this value must be of the same type for every arm in which
control reaches the end of the block:
~~~~
fn signum(x: int) -> int {
@ -575,9 +575,10 @@ fn signum(x: int) -> int {
## Pattern matching
Rust's `match` construct is a generalized, cleaned-up version of C's
`switch` construct. You provide it with a value and a number of *arms*,
each labelled with a pattern, and the code will attempt to match each pattern
in order. For the first one that matches, the arm is executed.
`switch` construct. You provide it with a value and a number of
*arms*, each labelled with a pattern, and the code compares the value
against each pattern in order until one matches. The matching pattern
executes its corresponding arm.
~~~~
# let my_number = 1;
@ -589,15 +590,19 @@ match my_number {
}
~~~~
There is no 'falling through' between arms, as in C—only one arm is
executed, and it doesn't have to explicitly `break` out of the
Unlike in C, there is no 'falling through' between arms: only one arm
executes, and it doesn't have to explicitly `break` out of the
construct when it is finished.
The part to the left of the arrow `=>` is called the *pattern*. Literals are
valid patterns and will match only their own value. The pipe operator
(`|`) can be used to assign multiple patterns to a single arm. Ranges
of numeric literal patterns can be expressed with two dots, as in `M..N`. The
underscore (`_`) is a wildcard pattern that matches everything.
A `match` arm consists of a *pattern*, then an arrow `=>`, followed by
an *action* (expression). Literals are valid patterns and match only
their own value. A single arm may match multiple different patterns by
combining them with the pipe operator (`|`), so long as every pattern
binds the same set of variables. Ranges of numeric literal patterns
can be expressed with two dots, as in `M..N`. The underscore (`_`) is
a wildcard pattern that matches any single value. The asterisk (`*`)
is a different wildcard that can match one or more fields in an `enum`
variant.
The patterns in an match arm are followed by a fat arrow, `=>`, then an
expression to evaluate. Each case is separated by commas. It's often
@ -612,13 +617,14 @@ match my_number {
}
~~~
`match` constructs must be *exhaustive*: they must have an arm covering every
possible case. For example, if the arm with the wildcard pattern was left off
in the above example, the typechecker would reject it.
`match` constructs must be *exhaustive*: they must have an arm
covering every possible case. For example, the typechecker would
reject the previous example if the arm with the wildcard pattern was
omitted.
A powerful application of pattern matching is *destructuring*, where
you use the matching to get at the contents of data types. Remember
that `(float, float)` is a tuple of two floats:
A powerful application of pattern matching is *destructuring*:
matching in order to bind names to the contents of data
types. Remember that `(float, float)` is a tuple of two floats:
~~~~
fn angle(vector: (float, float)) -> float {
@ -631,37 +637,39 @@ fn angle(vector: (float, float)) -> float {
}
~~~~
A variable name in a pattern matches everything, *and* binds that name
to the value of the matched thing inside of the arm block. Thus, `(0f,
A variable name in a pattern matches any value, *and* binds that name
to the value of the matched value inside of the arm's action. Thus, `(0f,
y)` matches any tuple whose first element is zero, and binds `y` to
the second element. `(x, y)` matches any tuple, and binds both
elements to a variable.
elements to variables.
Any `match` arm can have a guard clause (written `if EXPR`), which is
an expression of type `bool` that determines, after the pattern is
found to match, whether the arm is taken or not. The variables bound
by the pattern are available in this guard expression.
Any `match` arm can have a guard clause (written `if EXPR`), called a
*pattern guard*, which is an expression of type `bool` that
determines, after the pattern is found to match, whether the arm is
taken or not. The variables bound by the pattern are in scope in this
guard expression. The first arm in the `angle` example shows an
example of a pattern guard.
You've already seen simple `let` bindings, but `let` is a little
fancier than you've been led to believe. It too supports destructuring
patterns. For example, you can say this to extract the fields from a
tuple, introducing two variables, `a` and `b`.
fancier than you've been led to believe. It, too, supports destructuring
patterns. For example, you can write this to extract the fields from a
tuple, introducing two variables at once: `a` and `b`.
~~~~
# fn get_tuple_of_two_ints() -> (int, int) { (1, 1) }
let (a, b) = get_tuple_of_two_ints();
~~~~
Let bindings only work with _irrefutable_ patterns, that is, patterns
Let bindings only work with _irrefutable_ patterns: that is, patterns
that can never fail to match. This excludes `let` from matching
literals and most enum variants.
literals and most `enum` variants.
## Loops
`while` produces a loop that runs as long as its given condition
(which must have type `bool`) evaluates to true. Inside a loop, the
keyword `break` can be used to abort the loop, and `loop` can be used
to abort the current iteration and continue with the next.
`while` denotes a loop that iterates as long as its given condition
(which must have type `bool`) evaluates to `true`. Inside a loop, the
keyword `break` aborts the loop, and `loop` aborts the current
iteration and continues with the next.
~~~~
let mut cake_amount = 8;
@ -670,7 +678,7 @@ while cake_amount > 0 {
}
~~~~
`loop` is the preferred way of writing `while true`:
`loop` denotes an infinite loop, and is the preferred way of writing `while true`:
~~~~
let mut x = 5;
@ -684,9 +692,8 @@ loop {
This code prints out a weird sequence of numbers and stops as soon as
it finds one that can be divided by five.
For more involved iteration, such as going over the elements of a
collection, Rust uses higher-order functions. We'll come back to those
in a moment.
For more involved iteration, such as enumerating the elements of a
collection, Rust uses [higher-order functions](#closures).
# Data structures