mirror of https://github.com/rust-lang/book
Edits to nostarch's chapter 3 edits
This commit is contained in:
parent
274fb463e0
commit
d281b7b062
|
@ -37,7 +37,8 @@ that value. To illustrate this, let’s generate a new project called *variables
|
|||
in your *projects* directory by using `cargo new variables`.
|
||||
|
||||
Then, in your new *variables* directory, open *src/main.rs* and replace its
|
||||
code with the following code; this code won’t compile just yet, we'll first examine the immutability error:
|
||||
code with the following code. This code won’t compile just yet, we'll first
|
||||
examine the immutability error.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -79,20 +80,21 @@ assign twice to immutable variable `x` ``, because you tried to assign a second
|
|||
value to the immutable `x` variable.
|
||||
|
||||
It’s important that we get compile-time errors when we attempt to change a
|
||||
value that's designated as immutable because this very situation
|
||||
can lead to bugs. If one part of our code operates on the assumption that a
|
||||
value will never change and another part of our code changes that value, it’s
|
||||
possible that the first part of the code won’t do what it was designed to do.
|
||||
The cause of this kind of bug can be difficult to track down after the fact,
|
||||
especially when the second piece of code changes the value only *sometimes*. The Rust compiler guarantees that when you state that a value won’t change,
|
||||
it really won’t change, soc you don’t have to keep track of it yourself. Your code
|
||||
is thus easier to reason through.
|
||||
value that’s designated as immutable because this very situation can lead to
|
||||
bugs. If one part of our code operates on the assumption that a value will
|
||||
never change and another part of our code changes that value, it’s possible
|
||||
that the first part of the code won’t do what it was designed to do. The cause
|
||||
of this kind of bug can be difficult to track down after the fact, especially
|
||||
when the second piece of code changes the value only *sometimes*. The Rust
|
||||
compiler guarantees that when you state a value won’t change, it really won’t
|
||||
change, so you don’t have to keep track of it yourself. Your code is thus
|
||||
easier to reason through.
|
||||
|
||||
But mutability can be very useful, and can make code more convenient to write. Variables are immutable only by default; as
|
||||
you did in Chapter 2, you can make them mutable by adding `mut` in front of the
|
||||
variable name. Adding `mut` also conveys
|
||||
intent to future readers of the code by indicating that other parts of the code
|
||||
will be changing this variable’s value.
|
||||
But mutability can be very useful, and can make code more convenient to write.
|
||||
Variables are immutable only by default; as you did in Chapter 2, you can make
|
||||
them mutable by adding `mut` in front of the variable name. Adding `mut` also
|
||||
conveys intent to future readers of the code by indicating that other parts of
|
||||
the code will be changing this variable’s value.
|
||||
|
||||
For example, let’s change *src/main.rs* to the following:
|
||||
|
||||
|
@ -119,25 +121,26 @@ The value of x is: 6
|
|||
```
|
||||
|
||||
We’re allowed to change the value that `x` binds to from `5` to `6` when `mut`
|
||||
is used. There are multiple trade-offs to consider in addition to the prevention of
|
||||
bugs. For example, in cases where you’re using large data structures, mutating
|
||||
an instance in place may be faster than copying and returning newly allocated
|
||||
instances. With smaller data structures, creating new instances and writing in
|
||||
a more functional programming style may be easier to think through, so lower
|
||||
performance might be a worthwhile penalty for gaining that clarity.
|
||||
is used. There are multiple trade-offs to consider in addition to the
|
||||
prevention of bugs. For example, in cases where you’re using large data
|
||||
structures, mutating an instance in place may be faster than copying and
|
||||
returning newly allocated instances. With smaller data structures, creating new
|
||||
instances and writing in a more functional programming style may be easier to
|
||||
think through, so lower performance might be a worthwhile penalty for gaining
|
||||
that clarity.
|
||||
|
||||
### Constants
|
||||
|
||||
Like
|
||||
immutable variables, *constants* are values that are bound to a name and are not
|
||||
allowed to change, but there are a few differences between constants and
|
||||
variables.
|
||||
Like immutable variables, *constants* are values that are bound to a name and
|
||||
are not allowed to change, but there are a few differences between constants
|
||||
and variables.
|
||||
|
||||
First, you aren’t allowed to use `mut` with constants. Constants aren’t just
|
||||
immutable by default—they’re always immutable. You declare constants using the `const` keyword instead of the `let` keyword,
|
||||
and the type of the value *must* be annotated. We’re about to cover types and
|
||||
type annotations in the next section, “Data Types,” so don’t worry about the
|
||||
details right now. Just know that you must always annotate the type.
|
||||
immutable by default—they’re always immutable. You declare constants using the
|
||||
`const` keyword instead of the `let` keyword, and the type of the value *must*
|
||||
be annotated. We’re about to cover types and type annotations in the next
|
||||
section, “Data Types,” so don’t worry about the details right now. Just know
|
||||
that you must always annotate the type.
|
||||
|
||||
Constants can be declared in any scope, including the global scope, which makes
|
||||
them useful for values that many parts of code need to know about.
|
||||
|
@ -151,14 +154,14 @@ Here’s an example of a constant declaration:
|
|||
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
||||
```
|
||||
|
||||
The constant’s name is `THREE_HOURS_IN_SECONDS` and its value is set to the result of multiplying 60
|
||||
(the number of seconds in a minute) by 60 (the number of minutes in an hour) by
|
||||
3 (the number of hours we want to count in this program).
|
||||
Rust’s naming convention for constants is to use all uppercase with underscores
|
||||
between words. The compiler is able to evaluate a limited set of operations at
|
||||
compile time, which lets us choose to write out this value in a way that’s
|
||||
easier to understand and verify, rather than setting this constant to the value
|
||||
10,800. See the Rust Reference’s section on constant evaluation at
|
||||
The constant’s name is `THREE_HOURS_IN_SECONDS` and its value is set to the
|
||||
result of multiplying 60 (the number of seconds in a minute) by 60 (the number
|
||||
of minutes in an hour) by 3 (the number of hours we want to count in this
|
||||
program). Rust’s naming convention for constants is to use all uppercase with
|
||||
underscores between words. The compiler is able to evaluate a limited set of
|
||||
operations at compile time, which lets us choose to write out this value in a
|
||||
way that’s easier to understand and verify, rather than setting this constant
|
||||
to the value 10,800. See the Rust Reference’s section on constant evaluation at
|
||||
*https://doc.rust-lang.org/reference/const_eval.html* for more information on
|
||||
what operations can be used when declaring constants.
|
||||
|
||||
|
@ -175,11 +178,12 @@ hardcoded value needed to be updated in the future.
|
|||
|
||||
### Shadowing
|
||||
|
||||
As you saw in the guessing game tutorial in Chapter 2, you can declare a new variable with the
|
||||
same name as a previous variable. Rustaceans say that the first variable is
|
||||
*shadowed* by the second, which means that the second variable’s value is what
|
||||
the program sees when the variable is used. We can shadow a variable by using
|
||||
the same variable’s name and repeating the use of the `let` keyword as follows:
|
||||
As you saw in the guessing game tutorial in Chapter 2, you can declare a new
|
||||
variable with the same name as a previous variable. Rustaceans say that the
|
||||
first variable is *shadowed* by the second, which means that the second
|
||||
variable’s value is what the program sees when the variable is used. We can
|
||||
shadow a variable by using the same variable’s name and repeating the use of
|
||||
the `let` keyword as follows:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -220,7 +224,14 @@ using the `let` keyword. By using `let`, we can perform a few transformations
|
|||
on a value but have the variable be immutable after those transformations have
|
||||
been completed.
|
||||
|
||||
<!--- so, to be clear, we're not overwriting the variable, so when the shadowing variable goes out of scope the earlier variables become visible to the compiler? --->
|
||||
<!--- so, to be clear, we're not overwriting the variable, so when the
|
||||
shadowing variable goes out of scope the earlier variables become visible to
|
||||
the compiler? --->
|
||||
<!-- Well, we *are* overwriting it *in the inner scope* -- there's no way to
|
||||
access the original value from the outer scope within the inner scope after
|
||||
the shadowing. But yes, shadowing only applies to the scope it happens in,
|
||||
which is what this example illustrates. Is there something that could be
|
||||
made clearer? /Carol -->
|
||||
|
||||
The other difference between `mut` and shadowing is that because we’re
|
||||
effectively creating a new variable when we use the `let` keyword again, we can
|
||||
|
@ -233,11 +244,11 @@ inputting space characters, and then we want to store that input as a number:
|
|||
let spaces = spaces.len();
|
||||
```
|
||||
|
||||
The first `spaces` variable is a string type
|
||||
and the second `spaces` variable is a number type. Shadowing thus spares us
|
||||
from having to come up with different names, such as `spaces_str` and
|
||||
`spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
|
||||
try to use `mut` for this, as shown here, we’ll get a compile-time error:
|
||||
The first `spaces` variable is a string type and the second `spaces` variable
|
||||
is a number type. Shadowing thus spares us from having to come up with
|
||||
different names, such as `spaces_str` and `spaces_num`; instead, we can reuse
|
||||
the simpler `spaces` name. However, if we try to use `mut` for this, as shown
|
||||
here, we’ll get a compile-time error:
|
||||
|
||||
```
|
||||
let mut spaces = " ";
|
||||
|
@ -304,8 +315,8 @@ An *integer* is a number without a fractional component. We used one integer
|
|||
type in Chapter 2, the `u32` type. This type declaration indicates that the
|
||||
value it’s associated with should be an unsigned integer (signed integer types
|
||||
start with `i`, instead of `u`) that takes up 32 bits of space. Table 3-1 shows
|
||||
the built-in integer types in Rust. We can use any of these variants to declare the type of an integer
|
||||
value.
|
||||
the built-in integer types in Rust. We can use any of these variants to declare
|
||||
the type of an integer value.
|
||||
|
||||
Table 3-1: Integer Types in Rust
|
||||
|
||||
|
@ -333,11 +344,14 @@ Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
|
|||
-128 to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1,
|
||||
so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which equals 0 to 255.
|
||||
|
||||
Additionally, the `isize` and `usize` types depend on the kind of computer your
|
||||
program is running on: 64 bits if you’re on a 64-bit architecture and 32 bits
|
||||
if you’re on a 32-bit architecture.
|
||||
Additionally, the `isize` and `usize` types depend on the architecture of the
|
||||
computer your program is running on, which is denoted in the table as "arch":
|
||||
64 bits if you’re on a 64-bit architecture and 32 bits if you’re on a 32-bit
|
||||
architecture.
|
||||
|
||||
<!--- what does arch refer to? /LC --->
|
||||
<!-- architecture-dependent, I've tried to specifically make that connection,
|
||||
is this better? /Carol -->
|
||||
|
||||
You can write integer literals in any of the forms shown in Table 3-2. Note
|
||||
that number literals that can be multiple numeric types allow a type suffix,
|
||||
|
@ -362,15 +376,14 @@ some sort of collection.
|
|||
|
||||
> ##### Integer Overflow
|
||||
>
|
||||
> Let’s say you have a variable of type `u8` that can hold values between 0 and 255.
|
||||
> If you try to change the variable to a value outside of that range, such
|
||||
> as 256, *integer overflow* will occur, which can result in one of two behaviors.
|
||||
> When you’re compiling in debug mode, Rust includes
|
||||
> checks for integer overflow that cause your program to *panic* at runtime if
|
||||
> this behavior occurs. Rust uses the term panicking when a program exits with
|
||||
> an error; we’ll discuss panics in more depth in the “Unrecoverable Errors
|
||||
> with `panic!`” section in
|
||||
> Chapter 9.
|
||||
> Let’s say you have a variable of type `u8` that can hold values between 0 and
|
||||
> 255. If you try to change the variable to a value outside of that range, such
|
||||
> as 256, *integer overflow* will occur, which can result in one of two
|
||||
> behaviors. When you’re compiling in debug mode, Rust includes checks for
|
||||
> integer overflow that cause your program to *panic* at runtime if this
|
||||
> behavior occurs. Rust uses the term panicking when a program exits with an
|
||||
> error; we’ll discuss panics in more depth in the “Unrecoverable Errors with
|
||||
> `panic!`” section in Chapter 9.
|
||||
>
|
||||
> When you’re compiling in release mode with the `--release` flag, Rust does
|
||||
> *not* include checks for integer overflow that cause panics. Instead, if
|
||||
|
@ -397,9 +410,11 @@ Rust also has two primitive types for *floating-point numbers*, which are
|
|||
numbers with decimal points. Rust’s floating-point types are `f32` and `f64`,
|
||||
which are 32 bits and 64 bits in size, respectively. The default type is `f64`
|
||||
because on modern CPUs it’s roughly the same speed as `f32` but is capable of
|
||||
more precision.
|
||||
more precision. All floating-point types are signed.
|
||||
|
||||
<!--- and these don't need to be designate as signed or unsigned? /LC --->
|
||||
<!-- Yes, it's a long story why, I've added a quick statement as such at the
|
||||
end of the previous paragraph, does that work? /Carol-->
|
||||
|
||||
Here’s an example that shows floating-point numbers in action:
|
||||
|
||||
|
@ -471,8 +486,8 @@ Flow” section.
|
|||
|
||||
#### The Character Type
|
||||
|
||||
Rust’s
|
||||
`char` type is the language’s most primitive alphabetic type. Here's one way to use it.
|
||||
Rust’s `char` type is the language’s most primitive alphabetic type. Here's
|
||||
some examples of declaring `char` values:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -484,15 +499,16 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Note that we specify `char` literals with
|
||||
single quotes, as opposed to string literals, which use double quotes. Rust’s `char` type is four bytes in size and represents a Unicode Scalar Value,
|
||||
which means it can represent a lot more than just ASCII. Accented letters;
|
||||
Chinese, Japanese, and Korean characters; emoji; and zero-width spaces are all
|
||||
valid `char` values in Rust. Unicode Scalar Values range from `U+0000` to
|
||||
`U+D7FF` and `U+E000` to `U+10FFFF` inclusive. However, a “character” isn’t
|
||||
really a concept in Unicode, so your human intuition for what a “character” is
|
||||
may not match up with what a `char` is in Rust. We’ll discuss this topic in
|
||||
detail in “Storing UTF-8 Encoded Text with Strings” in Chapter 8.
|
||||
Note that we specify `char` literals with single quotes, as opposed to string
|
||||
literals, which use double quotes. Rust’s `char` type is four bytes in size and
|
||||
represents a Unicode Scalar Value, which means it can represent a lot more than
|
||||
just ASCII. Accented letters; Chinese, Japanese, and Korean characters; emoji;
|
||||
and zero-width spaces are all valid `char` values in Rust. Unicode Scalar
|
||||
Values range from `U+0000` to `U+D7FF` and `U+E000` to `U+10FFFF` inclusive.
|
||||
However, a “character” isn’t really a concept in Unicode, so your human
|
||||
intuition for what a “character” is may not match up with what a `char` is in
|
||||
Rust. We’ll discuss this topic in detail in “Storing UTF-8 Encoded Text with
|
||||
Strings” in Chapter 8.
|
||||
|
||||
### Compound Types
|
||||
|
||||
|
@ -540,9 +556,8 @@ variables, `x`, `y`, and `z`. This is called *destructuring*, because it breaks
|
|||
the single tuple into three parts. Finally, the program prints the value of
|
||||
`y`, which is `6.4`.
|
||||
|
||||
We can access a tuple
|
||||
element directly by using a period (`.`) followed by the index of the value we
|
||||
want to access. For example:
|
||||
We can also access a tuple element directly by using a period (`.`) followed by
|
||||
the index of the value we want to access. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -570,11 +585,11 @@ return any other value.
|
|||
#### The Array Type
|
||||
|
||||
Another way to have a collection of multiple values is with an *array*. Unlike
|
||||
a tuple, every element of an array must have the same type. Unlike arrays in some other languages, arrays in Rust have a
|
||||
fixed length.
|
||||
a tuple, every element of an array must have the same type. Unlike arrays in
|
||||
some other languages, arrays in Rust have a fixed length.
|
||||
|
||||
We write the values in an array as a comma-separated list
|
||||
inside square brackets:
|
||||
We write the values in an array as a comma-separated list inside square
|
||||
brackets:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -585,24 +600,34 @@ fn main() {
|
|||
```
|
||||
|
||||
Arrays are useful when you want your data allocated on the stack rather than
|
||||
the heap (we will discuss the stack and the heap more in Chapter 4) or when
|
||||
you want to ensure you always have a fixed number of elements. An array isn’t
|
||||
as flexible as the vector type, though. A vector is a similar collection type
|
||||
the heap (we will discuss the stack and the heap more in Chapter 4) or when you
|
||||
want to ensure you always have a fixed number of elements. An array isn’t as
|
||||
flexible as the vector type, though. A vector is a similar collection type
|
||||
provided by the standard library that *is* allowed to grow or shrink in size.
|
||||
If you’re unsure whether to use an array or a vector, chances are you should use a
|
||||
vector. Chapter 8 discusses vectors in more detail.
|
||||
If you’re unsure whether to use an array or a vector, chances are you should
|
||||
use a vector. Chapter 8 discusses vectors in more detail.
|
||||
|
||||
However, arrays are more useful when you know the values will not need to change.
|
||||
For example, if you were coding the names of the month into a strucuter, you would use an array rather than a vector because you know it will always contain 12 elements:
|
||||
However, arrays are more useful when you know the number of elements will not
|
||||
need to change. For example, if you were using the names of the month in a
|
||||
program, you would probably use an array rather than a vector because you know
|
||||
it will always contain 12 elements:
|
||||
|
||||
```
|
||||
let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
"August", "September", "October", "November", "December"];
|
||||
```
|
||||
<!--- and is the benefit here that using the array prevents other coders making the mistake of somehow changing the values, while with a vector they could? /LC --->
|
||||
|
||||
You write an array’s type using square brackets with the type of each element, a semicolon, and then the number of
|
||||
elements in the array, like so:
|
||||
<!--- and is the benefit here that using the array prevents other coders making
|
||||
the mistake of somehow changing the values, while with a vector they could?
|
||||
/LC --->
|
||||
<!-- No, the benefit is that you know there will always be 12 elements in the
|
||||
array. Arrays can be mutable in that other parts of the code could change
|
||||
"January" to "Jan", but they couldn't add or remove a month. Does that make
|
||||
sense? I've changed the paragraph before the code to say "number of elements"
|
||||
instead of "values", does that clear it up? /Carol -->
|
||||
|
||||
You write an array’s type using square brackets with the type of each element,
|
||||
a semicolon, and then the number of elements in the array, like so:
|
||||
|
||||
```
|
||||
let a: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
|
@ -611,9 +636,9 @@ let a: [i32; 5] = [1, 2, 3, 4, 5];
|
|||
Here, `i32` is the type of each element. After the semicolon, the number `5`
|
||||
indicates the array contains five elements.
|
||||
|
||||
You can aslo initialize an array to contain the same
|
||||
value for each element by specifying the initial value, followed by a
|
||||
semicolon, and then the length of the array in square brackets, as shown here:
|
||||
You can also initialize an array to contain the same value for each element by
|
||||
specifying the initial value, followed by a semicolon, and then the length of
|
||||
the array in square brackets, as shown here:
|
||||
|
||||
```
|
||||
let a = [3; 5];
|
||||
|
@ -646,9 +671,9 @@ get the value `2` from index `[1]` in the array.
|
|||
|
||||
##### Invalid Array Element Access
|
||||
|
||||
Let's see what happens if you try to access an element of an array that is past the end
|
||||
of the array. Say you run this code,
|
||||
similar to the guessing game in Chapter 2, to get an array index from the user:
|
||||
Let's see what happens if you try to access an element of an array that is past
|
||||
the end of the array. Say you run this code, similar to the guessing game in
|
||||
Chapter 2, to get an array index from the user:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -730,9 +755,9 @@ fn another_function() {
|
|||
}
|
||||
```
|
||||
|
||||
We define a function in Rust by entering `fn` followed by a function name ans a set of parentheses.
|
||||
The curly brackets tell the compiler where the
|
||||
function body begins and ends.
|
||||
We define a function in Rust by entering `fn` followed by a function name and a
|
||||
set of parentheses. The curly brackets tell the compiler where the function
|
||||
body begins and ends.
|
||||
|
||||
We can call any function we’ve defined by entering its name followed by a set
|
||||
of parentheses. Because `another_function` is defined in the program, it can be
|
||||
|
@ -760,13 +785,13 @@ called and its message is printed.
|
|||
|
||||
### Parameters
|
||||
|
||||
We can define functions to have *parameters*, which are special variables
|
||||
that are part of a function’s signature. When a function has parameters, you
|
||||
can provide it with concrete values for those parameters. Technically, the
|
||||
concrete values are called *arguments*, but in casual conversation, people tend
|
||||
to use the words *parameter* and *argument* interchangeably for either the
|
||||
variables in a function’s definition or the concrete values passed in when you
|
||||
call a function.
|
||||
We can define functions to have *parameters*, which are special variables that
|
||||
are part of a function’s signature. When a function has parameters, you can
|
||||
provide it with concrete values for those parameters. Technically, the concrete
|
||||
values are called *arguments*, but in casual conversation, people tend to use
|
||||
the words *parameter* and *argument* interchangeably for either the variables
|
||||
in a function’s definition or the concrete values passed in when you call a
|
||||
function.
|
||||
|
||||
In this version of `another_function` we add a parameter:
|
||||
|
||||
|
@ -802,8 +827,8 @@ a deliberate decision in Rust’s design: requiring type annotations in function
|
|||
definitions means the compiler almost never needs you to use them elsewhere in
|
||||
the code to figure out what type you mean.
|
||||
|
||||
When defining multiple parameters, separate the parameter
|
||||
declarations with commas, like this:
|
||||
When defining multiple parameters, separate the parameter declarations with
|
||||
commas, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -840,20 +865,19 @@ the value for `unit_label`, the program output contains those values.
|
|||
### Statements and Expressions
|
||||
|
||||
Function bodies are made up of a series of statements optionally ending in an
|
||||
expression. So far, the functions we've covered haven't included an ending expression,
|
||||
but you have seen an expression as part of a statement. Because Rust is an
|
||||
expression-based language, this is an important distinction to understand.
|
||||
Other languages don’t have the same distinctions, so let’s look at what
|
||||
statements and expressions are and how their differences affect the bodies of
|
||||
functions.
|
||||
expression. So far, the functions we've covered haven't included an ending
|
||||
expression, but you have seen an expression as part of a statement. Because
|
||||
Rust is an expression-based language, this is an important distinction to
|
||||
understand. Other languages don’t have the same distinctions, so let’s look at
|
||||
what statements and expressions are and how their differences affect the bodies
|
||||
of functions.
|
||||
|
||||
*Statements* are
|
||||
instructions that perform some action and do not return a value. *Expressions*
|
||||
evaluate to a resulting value. Let’s look at some examples.
|
||||
*Statements* are instructions that perform some action and do not return a
|
||||
value. *Expressions* evaluate to a resulting value. Let’s look at some examples.
|
||||
|
||||
We’ve actually already used statements and expressions.
|
||||
Creating a variable and assigning a value to it with the `let` keyword is a
|
||||
statement. In Listing 3-1, `let y = 6;` is a statement.
|
||||
We’ve actually already used statements and expressions. Creating a variable and
|
||||
assigning a value to it with the `let` keyword is a statement. In Listing 3-1,
|
||||
`let y = 6;` is a statement.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -921,8 +945,8 @@ you’ll write in Rust. Consider a math operation, such as `5 + 6`, which is an
|
|||
expression that evaluates to the value `11`. Expressions can be part of
|
||||
statements: in Listing 3-1, the `6` in the statement `let y = 6;` is an
|
||||
expression that evaluates to the value `6`. Calling a function is an
|
||||
expression. Calling a macro is an expression. The block that we use to create
|
||||
new scopes with `{}` is an expression, for example:
|
||||
expression. Calling a macro is an expression. A new scope block created with
|
||||
curly brackets is an expression, for example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -949,9 +973,9 @@ This expression:
|
|||
```
|
||||
|
||||
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
|
||||
as part of the `let` statement. Note that the `x + 1` line doesn't have a semicolon at
|
||||
the end, unlike most of the lines you’ve seen so far. Expressions do
|
||||
not include ending semicolons. If you add a semicolon to the end of an
|
||||
as part of the `let` statement. Note that the `x + 1` line doesn't have a
|
||||
semicolon at the end, unlike most of the lines you’ve seen so far. Expressions
|
||||
do not include ending semicolons. If you add a semicolon to the end of an
|
||||
expression, you turn it into a statement, and it will then not return a value.
|
||||
Keep this in mind as you explore function return values and expressions next.
|
||||
|
||||
|
@ -1067,9 +1091,9 @@ would fix the error.
|
|||
## Comments
|
||||
|
||||
All programmers strive to make their code easy to understand, but sometimes
|
||||
extra explanation is warranted. In these cases, programmers leave
|
||||
*comments* in their source code that the compiler will ignore but people
|
||||
reading the source code may find useful.
|
||||
extra explanation is warranted. In these cases, programmers leave *comments* in
|
||||
their source code that the compiler will ignore but people reading the source
|
||||
code may find useful.
|
||||
|
||||
Here’s a simple comment:
|
||||
|
||||
|
@ -1114,11 +1138,10 @@ discuss in the “Publishing a Crate to Crates.io” section of Chapter 14.
|
|||
|
||||
## Control Flow
|
||||
|
||||
The ability to run some code depending on if a condition is true
|
||||
and or run some code repeatedly while a condition is true are basic
|
||||
building blocks in most programming languages. The most common constructs that
|
||||
let you control the flow of execution of Rust code are `if` expressions and
|
||||
loops.
|
||||
The ability to run some code depending on if a condition is true, or run some
|
||||
code repeatedly while a condition is true, are basic building blocks in most
|
||||
programming languages. The most common constructs that let you control the flow
|
||||
of execution of Rust code are `if` expressions and loops.
|
||||
|
||||
### `if` Expressions
|
||||
|
||||
|
@ -1143,14 +1166,13 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
All `if` expressions start with the keyword `if`, followed by a
|
||||
condition. In this case, the condition checks whether or not the variable
|
||||
`number` has a value less than 5. We place block of code to execute if the
|
||||
condition is true immediately after the condition inside curly
|
||||
brackets. Blocks of code associated with the conditions in `if` expressions are
|
||||
sometimes called *arms*, just like the arms in `match` expressions that we
|
||||
discussed in the “Comparing the Guess to the Secret Number” section of Chapter
|
||||
2.
|
||||
All `if` expressions start with the keyword `if`, followed by a condition. In
|
||||
this case, the condition checks whether or not the variable `number` has a
|
||||
value less than 5. We place block of code to execute if the condition is true
|
||||
immediately after the condition inside curly brackets. Blocks of code
|
||||
associated with the conditions in `if` expressions are sometimes called *arms*,
|
||||
just like the arms in `match` expressions that we discussed in the “Comparing
|
||||
the Guess to the Secret Number” section of Chapter 2.
|
||||
|
||||
Optionally, we can also include an `else` expression, which we chose
|
||||
to do here, to give the program an alternative block of code to execute should
|
||||
|
@ -1237,7 +1259,7 @@ Running this code will print `number was something other than zero`.
|
|||
|
||||
#### Handling Multiple Conditions with `else if`
|
||||
|
||||
You can set multiple conditions by combining `if` and `else` in an `else if`
|
||||
You can use multiple conditions by combining `if` and `else` in an `else if`
|
||||
expression. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
@ -1348,12 +1370,12 @@ error[E0308]: `if` and `else` have incompatible types
|
|||
|
||||
The expression in the `if` block evaluates to an integer, and the expression in
|
||||
the `else` block evaluates to a string. This won’t work because variables must
|
||||
have a single type, and Rust needs to know at compile time what type the `number`
|
||||
variable is, definitively, so it can verify at compile time that its type is
|
||||
valid everywhere we use `number`. Rust wouldn’t be able to do that if the type
|
||||
of `number` was only determined at runtime; the compiler would be more complex
|
||||
and would make fewer guarantees about the code if it had to keep track of
|
||||
multiple hypothetical types for any variable.
|
||||
have a single type, and Rust needs to know at compile time what type the
|
||||
`number` variable is, definitively. Knowing the type of `number` lets the
|
||||
compiler verify the type is valid everywhere we use `number`. Rust wouldn’t be
|
||||
able to do that if the type of `number` was only determined at runtime; the
|
||||
compiler would be more complex and would make fewer guarantees about the code
|
||||
if it had to keep track of multiple hypothetical types for any variable.
|
||||
|
||||
### Repetition with Loops
|
||||
|
||||
|
@ -1410,13 +1432,13 @@ executing the loop. Recall that we did this in the guessing game in the
|
|||
“Quitting After a Correct Guess” section of Chapter 2 to exit the program when
|
||||
the user won the game by guessing the correct number.
|
||||
|
||||
We also used `continue` in the guessing game, which in a
|
||||
loop tells the program to skip over any remaining code in this iteration of the
|
||||
loop and go to the next iteration.
|
||||
We also used `continue` in the guessing game, which in a loop tells the program
|
||||
to skip over any remaining code in this iteration of the loop and go to the
|
||||
next iteration.
|
||||
|
||||
If you have loops within loops, `break` and `continue` apply to the innermost
|
||||
loop at that point. You can optionally specify a *loop label* on a loop that we can
|
||||
then use with `break` or `continue` to specify that those keywords apply to
|
||||
loop at that point. You can optionally specify a *loop label* on a loop that we
|
||||
can then use with `break` or `continue` to specify that those keywords apply to
|
||||
the labeled loop instead of the innermost loop. Here’s an example with two
|
||||
nested loops:
|
||||
|
||||
|
@ -1467,11 +1489,11 @@ End count = 2
|
|||
#### Returning Values from Loops
|
||||
|
||||
One of the uses of a `loop` is to retry an operation you know might fail, such
|
||||
as checking whether a thread has completed its job. You might also need to
|
||||
pass the result of that operation out of the loop to the rest of your code. To do this, you can
|
||||
add the value you want returned after the `break` expression you use to stop
|
||||
the loop; that value will be returned out of the loop so you can use it, as
|
||||
shown here:
|
||||
as checking whether a thread has completed its job. You might also need to pass
|
||||
the result of that operation out of the loop to the rest of your code. To do
|
||||
this, you can add the value you want returned after the `break` expression you
|
||||
use to stop the loop; that value will be returned out of the loop so you can
|
||||
use it, as shown here:
|
||||
|
||||
```
|
||||
fn main() {
|
||||
|
@ -1499,14 +1521,14 @@ print the value in `result`, which in this case is 20.
|
|||
|
||||
#### Conditional Loops with `while`
|
||||
|
||||
A program will often need to evaluate a condition within a loop. While
|
||||
the condition is true, the loop runs. When the condition ceases to be true, the
|
||||
program calls `break`, stopping the loop. It's possible t implemen
|
||||
a structure like this using a combination of `loop`, `if`, `else`, and `break`; you could try that
|
||||
now in a program, if you’d like. However, this pattern is so common that Rust has a built-in language construct
|
||||
for it, called a `while` loop. In Listing 3-3 we use `while` to loop the program
|
||||
three times, counting down each time, and then, after the loop, print
|
||||
a message and exit.
|
||||
A program will often need to evaluate a condition within a loop. While the
|
||||
condition is true, the loop runs. When the condition ceases to be true, the
|
||||
program calls `break`, stopping the loop. It's possible to implement behavior
|
||||
like this using a combination of `loop`, `if`, `else`, and `break`; you could
|
||||
try that now in a program, if you’d like. However, this pattern is so common
|
||||
that Rust has a built-in language construct for it, called a `while` loop. In
|
||||
Listing 3-3, we use `while` to loop the program three times, counting down each
|
||||
time, and then, after the loop, print a message and exit.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -1532,8 +1554,9 @@ true, the code runs; otherwise, it exits the loop.
|
|||
|
||||
#### Looping Through a Collection with `for`
|
||||
|
||||
In theory, you can use the `while` construct to loop over the elements of a collection,
|
||||
such as an array. For example, the loop in Listing 3-4 counts up to 50 in tens.
|
||||
You can choose to use the `while` construct to loop over the elements of a
|
||||
collection, such as an array. For example, the loop in Listing 3-4 prints each
|
||||
element in the array `a`.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
|
@ -1573,11 +1596,11 @@ All five array values appear in the terminal, as expected. Even though `index`
|
|||
will reach a value of `5` at some point, the loop stops executing before trying
|
||||
to fetch a sixth value from the array.
|
||||
|
||||
However, this approach is error prone; we could cause the program to panic if the
|
||||
index value or test condition are incorrect. For example, if you changed the definition of the
|
||||
`a` array to have four elements but forgot to update the condition to `while
|
||||
index < 4`, the code would panic. It’s also slow, because the
|
||||
compiler adds runtime code to perform the conditional check of whether the
|
||||
However, this approach is error prone; we could cause the program to panic if
|
||||
the index value or test condition are incorrect. For example, if you changed
|
||||
the definition of the `a` array to have four elements but forgot to update the
|
||||
condition to `while index < 4`, the code would panic. It’s also slow, because
|
||||
the compiler adds runtime code to perform the conditional check of whether the
|
||||
index is within the bounds of the array on every iteration through the loop.
|
||||
|
||||
As a more concise alternative, you can use a `for` loop and execute some code
|
||||
|
@ -1602,17 +1625,17 @@ importantly, we’ve now increased the safety of the code and eliminated the
|
|||
chance of bugs that might result from going beyond the end of the array or not
|
||||
going far enough and missing some items.
|
||||
|
||||
Using the `for` loop, you wouldn’t need to
|
||||
remember to change any other code if you changed the number of values in the
|
||||
array, as you would with the method used in Listing 3-4.
|
||||
Using the `for` loop, you wouldn’t need to remember to change any other code if
|
||||
you changed the number of values in the array, as you would with the method
|
||||
used in Listing 3-4.
|
||||
|
||||
The safety and conciseness of `for` loops make them the most commonly used loop
|
||||
construct in Rust. Even in situations in which you want to run some code a
|
||||
certain number of times, as in the countdown example that used a `while` loop
|
||||
in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that
|
||||
would be to use a `Range`, provided by the standard library, which
|
||||
generates all numbers in sequence starting from one number and ending
|
||||
before another number.
|
||||
would be to use a `Range`, provided by the standard library, which generates
|
||||
all numbers in sequence starting from one number and ending before another
|
||||
number.
|
||||
|
||||
Here’s what the countdown would look like using a `for` loop and another method
|
||||
we’ve not yet talked about, `rev`, to reverse the range:
|
||||
|
|
|
@ -13,7 +13,8 @@ that value. To illustrate this, let’s generate a new project called *variables
|
|||
in your *projects* directory by using `cargo new variables`.
|
||||
|
||||
Then, in your new *variables* directory, open *src/main.rs* and replace its
|
||||
code with the following code that won’t compile just yet:
|
||||
code with the following code. This code won’t compile just yet, we'll first
|
||||
examine the immutability error.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -29,7 +30,7 @@ message, as shown in this output:
|
|||
```
|
||||
|
||||
This example shows how the compiler helps you find errors in your programs.
|
||||
Even though compiler errors can be frustrating, they only mean your program
|
||||
Compiler errors can be frustrating, but really they only mean your program
|
||||
isn’t safely doing what you want it to do yet; they do *not* mean that you’re
|
||||
not a good programmer! Experienced Rustaceans still get compiler errors.
|
||||
|
||||
|
@ -38,23 +39,21 @@ assign twice to immutable variable `x` ``, because you tried to assign a second
|
|||
value to the immutable `x` variable.
|
||||
|
||||
It’s important that we get compile-time errors when we attempt to change a
|
||||
value that we previously designated as immutable because this very situation
|
||||
can lead to bugs. If one part of our code operates on the assumption that a
|
||||
value will never change and another part of our code changes that value, it’s
|
||||
possible that the first part of the code won’t do what it was designed to do.
|
||||
The cause of this kind of bug can be difficult to track down after the fact,
|
||||
especially when the second piece of code changes the value only *sometimes*.
|
||||
value that’s designated as immutable because this very situation can lead to
|
||||
bugs. If one part of our code operates on the assumption that a value will
|
||||
never change and another part of our code changes that value, it’s possible
|
||||
that the first part of the code won’t do what it was designed to do. The cause
|
||||
of this kind of bug can be difficult to track down after the fact, especially
|
||||
when the second piece of code changes the value only *sometimes*. The Rust
|
||||
compiler guarantees that when you state a value won’t change, it really won’t
|
||||
change, so you don’t have to keep track of it yourself. Your code is thus
|
||||
easier to reason through.
|
||||
|
||||
In Rust, the compiler guarantees that when you state that a value won’t change,
|
||||
it really won’t change. That means that when you’re reading and writing code,
|
||||
you don’t have to keep track of how and where a value might change. Your code
|
||||
is thus easier to reason through.
|
||||
|
||||
But mutability can be very useful. Variables are immutable only by default; as
|
||||
you did in Chapter 2, you can make them mutable by adding `mut` in front of the
|
||||
variable name. In addition to allowing this value to change, `mut` conveys
|
||||
intent to future readers of the code by indicating that other parts of the code
|
||||
will be changing this variable’s value.
|
||||
But mutability can be very useful, and can make code more convenient to write.
|
||||
Variables are immutable only by default; as you did in Chapter 2, you can make
|
||||
them mutable by adding `mut` in front of the variable name. Adding `mut` also
|
||||
conveys intent to future readers of the code by indicating that other parts of
|
||||
the code will be changing this variable’s value.
|
||||
|
||||
For example, let’s change *src/main.rs* to the following:
|
||||
|
||||
|
@ -71,32 +70,26 @@ When we run the program now, we get this:
|
|||
```
|
||||
|
||||
We’re allowed to change the value that `x` binds to from `5` to `6` when `mut`
|
||||
is used. In some cases, you’ll want to make a variable mutable because it makes
|
||||
the code more convenient to write than if it had only immutable variables.
|
||||
is used. There are multiple trade-offs to consider in addition to the
|
||||
prevention of bugs. For example, in cases where you’re using large data
|
||||
structures, mutating an instance in place may be faster than copying and
|
||||
returning newly allocated instances. With smaller data structures, creating new
|
||||
instances and writing in a more functional programming style may be easier to
|
||||
think through, so lower performance might be a worthwhile penalty for gaining
|
||||
that clarity.
|
||||
|
||||
There are multiple trade-offs to consider in addition to the prevention of
|
||||
bugs. For example, in cases where you’re using large data structures, mutating
|
||||
an instance in place may be faster than copying and returning newly allocated
|
||||
instances. With smaller data structures, creating new instances and writing in
|
||||
a more functional programming style may be easier to think through, so lower
|
||||
performance might be a worthwhile penalty for gaining that clarity.
|
||||
### Constants
|
||||
|
||||
### Differences Between Variables and Constants
|
||||
|
||||
Being unable to change the value of a variable might have reminded you of
|
||||
another programming concept that most other languages have: *constants*. Like
|
||||
immutable variables, constants are values that are bound to a name and are not
|
||||
allowed to change, but there are a few differences between constants and
|
||||
variables.
|
||||
Like immutable variables, *constants* are values that are bound to a name and
|
||||
are not allowed to change, but there are a few differences between constants
|
||||
and variables.
|
||||
|
||||
First, you aren’t allowed to use `mut` with constants. Constants aren’t just
|
||||
immutable by default—they’re always immutable.
|
||||
|
||||
You declare constants using the `const` keyword instead of the `let` keyword,
|
||||
and the type of the value *must* be annotated. We’re about to cover types and
|
||||
type annotations in the next section, [“Data Types,”][data-types]<!-- ignore
|
||||
--> so don’t worry about the details right now. Just know that you must always
|
||||
annotate the type.
|
||||
immutable by default—they’re always immutable. You declare constants using the
|
||||
`const` keyword instead of the `let` keyword, and the type of the value *must*
|
||||
be annotated. We’re about to cover types and type annotations in the next
|
||||
section, [“Data Types,”][data-types]<!-- ignore --> so don’t worry about the
|
||||
details right now. Just know that you must always annotate the type.
|
||||
|
||||
Constants can be declared in any scope, including the global scope, which makes
|
||||
them useful for values that many parts of code need to know about.
|
||||
|
@ -104,21 +97,22 @@ them useful for values that many parts of code need to know about.
|
|||
The last difference is that constants may be set only to a constant expression,
|
||||
not the result of a value that could only be computed at runtime.
|
||||
|
||||
Here’s an example of a constant declaration where the constant’s name is
|
||||
`THREE_HOURS_IN_SECONDS` and its value is set to the result of multiplying 60
|
||||
(the number of seconds in a minute) by 60 (the number of minutes in an hour) by
|
||||
3 (the number of hours we want to count in this program):
|
||||
Here’s an example of a constant declaration:
|
||||
|
||||
```rust
|
||||
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
||||
```
|
||||
|
||||
Rust’s naming convention for constants is to use all uppercase with underscores
|
||||
between words. The compiler is able to evaluate a limited set of operations at
|
||||
compile time, which lets us choose to write out this value in a way that’s
|
||||
easier to understand and verify, rather than setting this constant to the value
|
||||
10,800. See the [Rust Reference’s section on constant evaluation][const-eval]
|
||||
for more information on what operations can be used when declaring constants.
|
||||
The constant’s name is `THREE_HOURS_IN_SECONDS` and its value is set to the
|
||||
result of multiplying 60 (the number of seconds in a minute) by 60 (the number
|
||||
of minutes in an hour) by 3 (the number of hours we want to count in this
|
||||
program). Rust’s naming convention for constants is to use all uppercase with
|
||||
underscores between words. The compiler is able to evaluate a limited set of
|
||||
operations at compile time, which lets us choose to write out this value in a
|
||||
way that’s easier to understand and verify, rather than setting this constant
|
||||
to the value 10,800. See the [Rust Reference’s section on constant
|
||||
evaluation][const-eval] for more information on what operations can be used
|
||||
when declaring constants.
|
||||
|
||||
Constants are valid for the entire time a program runs, within the scope they
|
||||
were declared in. This property makes constants useful for values in your
|
||||
|
@ -133,13 +127,13 @@ hardcoded value needed to be updated in the future.
|
|||
|
||||
### Shadowing
|
||||
|
||||
As you saw in the guessing game tutorial in the [“Comparing the Guess to the
|
||||
Secret Number”][comparing-the-guess-to-the-secret-number]<!-- ignore -->
|
||||
section in Chapter 2, you can declare a new variable with the same name as a
|
||||
previous variable. Rustaceans say that the first variable is *shadowed* by the
|
||||
second, which means that the second variable’s value is what the program sees when the
|
||||
variable is used. We can shadow a variable by using the same variable’s name
|
||||
and repeating the use of the `let` keyword as follows:
|
||||
As you saw in the guessing game tutorial in [Chapter
|
||||
2][comparing-the-guess-to-the-secret-number]<!-- ignore -->, you can declare a
|
||||
new variable with the same name as a previous variable. Rustaceans say that the
|
||||
first variable is *shadowed* by the second, which means that the second
|
||||
variable’s value is what the program sees when the variable is used. We can
|
||||
shadow a variable by using the same variable’s name and repeating the use of
|
||||
the `let` keyword as follows:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -168,18 +162,17 @@ The other difference between `mut` and shadowing is that because we’re
|
|||
effectively creating a new variable when we use the `let` keyword again, we can
|
||||
change the type of the value but reuse the same name. For example, say our
|
||||
program asks a user to show how many spaces they want between some text by
|
||||
inputting space characters, but we really want to store that input as a number:
|
||||
inputting space characters, and then we want to store that input as a number:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-04-shadowing-can-change-types/src/main.rs:here}}
|
||||
```
|
||||
|
||||
This construct is allowed because the first `spaces` variable is a string type
|
||||
and the second `spaces` variable, which is a brand-new variable that happens to
|
||||
have the same name as the first one, is a number type. Shadowing thus spares us
|
||||
from having to come up with different names, such as `spaces_str` and
|
||||
`spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
|
||||
try to use `mut` for this, as shown here, we’ll get a compile-time error:
|
||||
The first `spaces` variable is a string type and the second `spaces` variable
|
||||
is a number type. Shadowing thus spares us from having to come up with
|
||||
different names, such as `spaces_str` and `spaces_num`; instead, we can reuse
|
||||
the simpler `spaces` name. However, if we try to use `mut` for this, as shown
|
||||
here, we’ll get a compile-time error:
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/src/main.rs:here}}
|
||||
|
|
|
@ -38,9 +38,8 @@ An *integer* is a number without a fractional component. We used one integer
|
|||
type in Chapter 2, the `u32` type. This type declaration indicates that the
|
||||
value it’s associated with should be an unsigned integer (signed integer types
|
||||
start with `i`, instead of `u`) that takes up 32 bits of space. Table 3-1 shows
|
||||
the built-in integer types in Rust. Each variant in the Signed and Unsigned
|
||||
columns (for example, `i16`) can be used to declare the type of an integer
|
||||
value.
|
||||
the built-in integer types in Rust. We can use any of these variants to declare
|
||||
the type of an integer value.
|
||||
|
||||
<span class="caption">Table 3-1: Integer Types in Rust</span>
|
||||
|
||||
|
@ -70,9 +69,10 @@ Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
|
|||
-128 to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1,
|
||||
so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which equals 0 to 255.
|
||||
|
||||
Additionally, the `isize` and `usize` types depend on the kind of computer your
|
||||
program is running on: 64 bits if you’re on a 64-bit architecture and 32 bits
|
||||
if you’re on a 32-bit architecture.
|
||||
Additionally, the `isize` and `usize` types depend on the architecture of the
|
||||
computer your program is running on, which is denoted in the table as "arch":
|
||||
64 bits if you’re on a 64-bit architecture and 32 bits if you’re on a 32-bit
|
||||
architecture.
|
||||
|
||||
You can write integer literals in any of the forms shown in Table 3-2. Note
|
||||
that number literals that can be multiple numeric types allow a type suffix,
|
||||
|
@ -97,15 +97,15 @@ some sort of collection.
|
|||
|
||||
> ##### Integer Overflow
|
||||
>
|
||||
> Let’s say you have a variable of type `u8` that can hold values between 0 and 255.
|
||||
> If you try to change the variable to a value outside of that range, such
|
||||
> as 256, *integer overflow* will occur. Rust has some interesting rules
|
||||
> involving this behavior. When you’re compiling in debug mode, Rust includes
|
||||
> checks for integer overflow that cause your program to *panic* at runtime if
|
||||
> this behavior occurs. Rust uses the term panicking when a program exits with
|
||||
> an error; we’ll discuss panics in more depth in the [“Unrecoverable Errors
|
||||
> with `panic!`”][unrecoverable-errors-with-panic]<!-- ignore --> section in
|
||||
> Chapter 9.
|
||||
> Let’s say you have a variable of type `u8` that can hold values between 0 and
|
||||
> 255. If you try to change the variable to a value outside of that range, such
|
||||
> as 256, *integer overflow* will occur, which can result in one of two
|
||||
> behaviors. When you’re compiling in debug mode, Rust includes checks for
|
||||
> integer overflow that cause your program to *panic* at runtime if this
|
||||
> behavior occurs. Rust uses the term panicking when a program exits with an
|
||||
> error; we’ll discuss panics in more depth in the [“Unrecoverable Errors with
|
||||
> `panic!`”][unrecoverable-errors-with-panic]<!-- ignore --> section in Chapter
|
||||
> 9.
|
||||
>
|
||||
> When you’re compiling in release mode with the `--release` flag, Rust does
|
||||
> *not* include checks for integer overflow that cause panics. Instead, if
|
||||
|
@ -117,7 +117,7 @@ some sort of collection.
|
|||
> have. Relying on integer overflow’s wrapping behavior is considered an error.
|
||||
>
|
||||
> To explicitly handle the possibility of overflow, you can use these families
|
||||
> of methods that the standard library provides on primitive numeric types:
|
||||
> of methods provided by the standard library for primitive numeric types:
|
||||
>
|
||||
> - Wrap in all modes with the `wrapping_*` methods, such as `wrapping_add`
|
||||
> - Return the `None` value if there is overflow with the `checked_*` methods
|
||||
|
@ -132,7 +132,7 @@ Rust also has two primitive types for *floating-point numbers*, which are
|
|||
numbers with decimal points. Rust’s floating-point types are `f32` and `f64`,
|
||||
which are 32 bits and 64 bits in size, respectively. The default type is `f64`
|
||||
because on modern CPUs it’s roughly the same speed as `f32` but is capable of
|
||||
more precision.
|
||||
more precision. All floating-point types are signed.
|
||||
|
||||
Here’s an example that shows floating-point numbers in action:
|
||||
|
||||
|
@ -180,10 +180,8 @@ Flow”][control-flow]<!-- ignore --> section.
|
|||
|
||||
#### The Character Type
|
||||
|
||||
So far we’ve worked only with numbers, but Rust supports letters too. Rust’s
|
||||
`char` type is the language’s most primitive alphabetic type, and the following
|
||||
code shows one way to use it. (Note that `char` literals are specified with
|
||||
single quotes, as opposed to string literals, which use double quotes.)
|
||||
Rust’s `char` type is the language’s most primitive alphabetic type. Here's
|
||||
some examples of declaring `char` values:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -191,15 +189,16 @@ single quotes, as opposed to string literals, which use double quotes.)
|
|||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-09-char/src/main.rs}}
|
||||
```
|
||||
|
||||
Rust’s `char` type is four bytes in size and represents a Unicode Scalar Value,
|
||||
which means it can represent a lot more than just ASCII. Accented letters;
|
||||
Chinese, Japanese, and Korean characters; emoji; and zero-width spaces are all
|
||||
valid `char` values in Rust. Unicode Scalar Values range from `U+0000` to
|
||||
`U+D7FF` and `U+E000` to `U+10FFFF` inclusive. However, a “character” isn’t
|
||||
really a concept in Unicode, so your human intuition for what a “character” is
|
||||
may not match up with what a `char` is in Rust. We’ll discuss this topic in
|
||||
detail in [“Storing UTF-8 Encoded Text with Strings”][strings]<!-- ignore -->
|
||||
in Chapter 8.
|
||||
Note that we specify `char` literals with single quotes, as opposed to string
|
||||
literals, which use double quotes. Rust’s `char` type is four bytes in size and
|
||||
represents a Unicode Scalar Value, which means it can represent a lot more than
|
||||
just ASCII. Accented letters; Chinese, Japanese, and Korean characters; emoji;
|
||||
and zero-width spaces are all valid `char` values in Rust. Unicode Scalar
|
||||
Values range from `U+0000` to `U+D7FF` and `U+E000` to `U+10FFFF` inclusive.
|
||||
However, a “character” isn’t really a concept in Unicode, so your human
|
||||
intuition for what a “character” is may not match up with what a `char` is in
|
||||
Rust. We’ll discuss this topic in detail in [“Storing UTF-8 Encoded Text with
|
||||
Strings”][strings]<!-- ignore --> in Chapter 8.
|
||||
|
||||
### Compound Types
|
||||
|
||||
|
@ -239,9 +238,8 @@ variables, `x`, `y`, and `z`. This is called *destructuring*, because it breaks
|
|||
the single tuple into three parts. Finally, the program prints the value of
|
||||
`y`, which is `6.4`.
|
||||
|
||||
In addition to destructuring through pattern matching, we can access a tuple
|
||||
element directly by using a period (`.`) followed by the index of the value we
|
||||
want to access. For example:
|
||||
We can also access a tuple element directly by using a period (`.`) followed by
|
||||
the index of the value we want to access. For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -249,7 +247,7 @@ want to access. For example:
|
|||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-12-tuple-indexing/src/main.rs}}
|
||||
```
|
||||
|
||||
This program creates a tuple, `x`, and then makes new variables for each
|
||||
This program creates the tuple `x` and then makes new variables for each
|
||||
element by using their respective indices. As with most programming languages,
|
||||
the first index in a tuple is 0.
|
||||
|
||||
|
@ -261,12 +259,11 @@ return any other value.
|
|||
#### The Array Type
|
||||
|
||||
Another way to have a collection of multiple values is with an *array*. Unlike
|
||||
a tuple, every element of an array must have the same type. Arrays in Rust are
|
||||
different from arrays in some other languages because arrays in Rust have a
|
||||
fixed length, like tuples.
|
||||
a tuple, every element of an array must have the same type. Unlike arrays in
|
||||
some other languages, arrays in Rust have a fixed length.
|
||||
|
||||
In Rust, the values going into an array are written as a comma-separated list
|
||||
inside square brackets:
|
||||
We write the values in an array as a comma-separated list inside square
|
||||
brackets:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -275,26 +272,26 @@ inside square brackets:
|
|||
```
|
||||
|
||||
Arrays are useful when you want your data allocated on the stack rather than
|
||||
the heap (we will discuss the stack and the heap more in Chapter 4) or when
|
||||
you want to ensure you always have a fixed number of elements. An array isn’t
|
||||
as flexible as the vector type, though. A vector is a similar collection type
|
||||
provided by the standard library that *is* allowed to grow or shrink in size.
|
||||
If you’re unsure whether to use an array or a vector, you should probably use a
|
||||
vector. Chapter 8 discusses vectors in more detail.
|
||||
the heap (we will discuss the stack and the heap more in [Chapter
|
||||
4][stack-and-heap]<!-- ignore -->) or when you want to ensure you always have a
|
||||
fixed number of elements. An array isn’t as flexible as the vector type,
|
||||
though. A vector is a similar collection type provided by the standard library
|
||||
that *is* allowed to grow or shrink in size. If you’re unsure whether to use an
|
||||
array or a vector, chances are you should use a vector. [Chapter
|
||||
8][vectors]<!-- ignore --> discusses vectors in more detail.
|
||||
|
||||
An example of when you might want to use an array rather than a vector is in a
|
||||
program that needs to know the names of the months of the year. It’s very
|
||||
unlikely that such a program will need to add or remove months, so you can use
|
||||
an array because you know it will always contain 12 elements:
|
||||
However, arrays are more useful when you know the number of elements will not
|
||||
need to change. For example, if you were using the names of the month in a
|
||||
program, you would probably use an array rather than a vector because you know
|
||||
it will always contain 12 elements:
|
||||
|
||||
```rust
|
||||
let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
"August", "September", "October", "November", "December"];
|
||||
```
|
||||
|
||||
You would write an array’s type by using square brackets, and within the
|
||||
brackets include the type of each element, a semicolon, and then the number of
|
||||
elements in the array, like so:
|
||||
You write an array’s type using square brackets with the type of each element,
|
||||
a semicolon, and then the number of elements in the array, like so:
|
||||
|
||||
```rust
|
||||
let a: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
|
@ -303,10 +300,9 @@ let a: [i32; 5] = [1, 2, 3, 4, 5];
|
|||
Here, `i32` is the type of each element. After the semicolon, the number `5`
|
||||
indicates the array contains five elements.
|
||||
|
||||
Writing an array’s type this way looks similar to an alternative syntax for
|
||||
initializing an array: if you want to create an array that contains the same
|
||||
value for each element, you can specify the initial value, followed by a
|
||||
semicolon, and then the length of the array in square brackets, as shown here:
|
||||
You can also initialize an array to contain the same value for each element by
|
||||
specifying the initial value, followed by a semicolon, and then the length of
|
||||
the array in square brackets, as shown here:
|
||||
|
||||
```rust
|
||||
let a = [3; 5];
|
||||
|
@ -334,9 +330,9 @@ get the value `2` from index `[1]` in the array.
|
|||
|
||||
##### Invalid Array Element Access
|
||||
|
||||
What happens if you try to access an element of an array that is past the end
|
||||
of the array? Say you change the example to the following, which uses code
|
||||
similar to the guessing game in Chapter 2 to get an array index from the user:
|
||||
Let's see what happens if you try to access an element of an array that is past
|
||||
the end of the array. Say you run this code, similar to the guessing game in
|
||||
Chapter 2, to get an array index from the user:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -379,6 +375,8 @@ continuing. Chapter 9 discusses more of Rust’s error handling.
|
|||
ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number
|
||||
[control-flow]: ch03-05-control-flow.html#control-flow
|
||||
[strings]: ch08-02-strings.html#storing-utf-8-encoded-text-with-strings
|
||||
[stack-and-heap]: ch04-01-what-is-ownership.html#the-stack-and-the-heap
|
||||
[vectors]: ch08-01-vectors.html
|
||||
[unrecoverable-errors-with-panic]: ch09-01-unrecoverable-errors-with-panic.html
|
||||
[wrapping]: ../std/num/struct.Wrapping.html
|
||||
[appendix_b]: appendix-02-operators.md
|
||||
|
|
|
@ -6,7 +6,7 @@ point of many programs. You’ve also seen the `fn` keyword, which allows you to
|
|||
declare new functions.
|
||||
|
||||
Rust code uses *snake case* as the conventional style for function and variable
|
||||
names. In snake case, all letters are lowercase and underscores separate words.
|
||||
names, in which all letters are lowercase and underscores separate words.
|
||||
Here’s a program that contains an example function definition:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
@ -15,9 +15,9 @@ Here’s a program that contains an example function definition:
|
|||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-16-functions/src/main.rs}}
|
||||
```
|
||||
|
||||
Function definitions in Rust start with `fn` and have a set of parentheses
|
||||
after the function name. The curly brackets tell the compiler where the
|
||||
function body begins and ends.
|
||||
We define a function in Rust by entering `fn` followed by a function name and a
|
||||
set of parentheses. The curly brackets tell the compiler where the function
|
||||
body begins and ends.
|
||||
|
||||
We can call any function we’ve defined by entering its name followed by a set
|
||||
of parentheses. Because `another_function` is defined in the program, it can be
|
||||
|
@ -38,18 +38,17 @@ The lines execute in the order in which they appear in the `main` function.
|
|||
First, the “Hello, world!” message prints, and then `another_function` is
|
||||
called and its message is printed.
|
||||
|
||||
### Function Parameters
|
||||
### Parameters
|
||||
|
||||
Functions can also be defined to have *parameters*, which are special variables
|
||||
that are part of a function’s signature. When a function has parameters, you
|
||||
can provide it with concrete values for those parameters. Technically, the
|
||||
concrete values are called *arguments*, but in casual conversation, people tend
|
||||
to use the words *parameter* and *argument* interchangeably for either the
|
||||
variables in a function’s definition or the concrete values passed in when you
|
||||
call a function.
|
||||
We can define functions to have *parameters*, which are special variables that
|
||||
are part of a function’s signature. When a function has parameters, you can
|
||||
provide it with concrete values for those parameters. Technically, the concrete
|
||||
values are called *arguments*, but in casual conversation, people tend to use
|
||||
the words *parameter* and *argument* interchangeably for either the variables
|
||||
in a function’s definition or the concrete values passed in when you call a
|
||||
function.
|
||||
|
||||
The following rewritten version of `another_function` shows what parameters
|
||||
look like in Rust:
|
||||
In this version of `another_function` we add a parameter:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -64,7 +63,7 @@ Try running this program; you should get the following output:
|
|||
```
|
||||
|
||||
The declaration of `another_function` has one parameter named `x`. The type of
|
||||
`x` is specified as `i32`. When `5` is passed to `another_function`, the
|
||||
`x` is specified as `i32`. When we pass `5` in to `another_function`, the
|
||||
`println!` macro puts `5` where the pair of curly brackets were in the format
|
||||
string.
|
||||
|
||||
|
@ -73,8 +72,8 @@ a deliberate decision in Rust’s design: requiring type annotations in function
|
|||
definitions means the compiler almost never needs you to use them elsewhere in
|
||||
the code to figure out what type you mean.
|
||||
|
||||
When you want a function to have multiple parameters, separate the parameter
|
||||
declarations with commas, like this:
|
||||
When defining multiple parameters, separate the parameter declarations with
|
||||
commas, like this:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -98,22 +97,22 @@ run`:
|
|||
Because we called the function with `5` as the value for `value` and `'h'` as
|
||||
the value for `unit_label`, the program output contains those values.
|
||||
|
||||
### Function Bodies Contain Statements and Expressions
|
||||
### Statements and Expressions
|
||||
|
||||
Function bodies are made up of a series of statements optionally ending in an
|
||||
expression. So far, we’ve only covered functions without an ending expression,
|
||||
but you have seen an expression as part of a statement. Because Rust is an
|
||||
expression-based language, this is an important distinction to understand.
|
||||
Other languages don’t have the same distinctions, so let’s look at what
|
||||
statements and expressions are and how their differences affect the bodies of
|
||||
functions.
|
||||
expression. So far, the functions we've covered haven't included an ending
|
||||
expression, but you have seen an expression as part of a statement. Because
|
||||
Rust is an expression-based language, this is an important distinction to
|
||||
understand. Other languages don’t have the same distinctions, so let’s look at
|
||||
what statements and expressions are and how their differences affect the bodies
|
||||
of functions.
|
||||
|
||||
We’ve actually already used statements and expressions. *Statements* are
|
||||
instructions that perform some action and do not return a value. *Expressions*
|
||||
evaluate to a resulting value. Let’s look at some examples.
|
||||
*Statements* are instructions that perform some action and do not return a
|
||||
value. *Expressions* evaluate to a resulting value. Let’s look at some examples.
|
||||
|
||||
Creating a variable and assigning a value to it with the `let` keyword is a
|
||||
statement. In Listing 3-1, `let y = 6;` is a statement.
|
||||
We’ve actually already used statements and expressions. Creating a variable and
|
||||
assigning a value to it with the `let` keyword is a statement. In Listing 3-1,
|
||||
`let y = 6;` is a statement.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -152,8 +151,8 @@ you’ll write in Rust. Consider a math operation, such as `5 + 6`, which is an
|
|||
expression that evaluates to the value `11`. Expressions can be part of
|
||||
statements: in Listing 3-1, the `6` in the statement `let y = 6;` is an
|
||||
expression that evaluates to the value `6`. Calling a function is an
|
||||
expression. Calling a macro is an expression. The block that we use to create
|
||||
new scopes, `{}`, is an expression, for example:
|
||||
expression. Calling a macro is an expression. A new scope block created with
|
||||
curly brackets is an expression, for example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -171,10 +170,10 @@ This expression:
|
|||
```
|
||||
|
||||
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
|
||||
as part of the `let` statement. Note the `x + 1` line without a semicolon at
|
||||
the end, which is unlike most of the lines you’ve seen so far. Expressions do
|
||||
not include ending semicolons. If you add a semicolon to the end of an
|
||||
expression, you turn it into a statement, which will then not return a value.
|
||||
as part of the `let` statement. Note that the `x + 1` line doesn't have a
|
||||
semicolon at the end, unlike most of the lines you’ve seen so far. Expressions
|
||||
do not include ending semicolons. If you add a semicolon to the end of an
|
||||
expression, you turn it into a statement, and it will then not return a value.
|
||||
Keep this in mind as you explore function return values and expressions next.
|
||||
|
||||
### Functions with Return Values
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
## Comments
|
||||
|
||||
All programmers strive to make their code easy to understand, but sometimes
|
||||
extra explanation is warranted. In these cases, programmers leave notes, or
|
||||
*comments*, in their source code that the compiler will ignore but people
|
||||
reading the source code may find useful.
|
||||
extra explanation is warranted. In these cases, programmers leave *comments* in
|
||||
their source code that the compiler will ignore but people reading the source
|
||||
code may find useful.
|
||||
|
||||
Here’s a simple comment:
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
## Control Flow
|
||||
|
||||
Deciding whether or not to run some code depending on if a condition is true
|
||||
and deciding to run some code repeatedly while a condition is true are basic
|
||||
building blocks in most programming languages. The most common constructs that
|
||||
let you control the flow of execution of Rust code are `if` expressions and
|
||||
loops.
|
||||
The ability to run some code depending on if a condition is true, or run some
|
||||
code repeatedly while a condition is true, are basic building blocks in most
|
||||
programming languages. The most common constructs that let you control the flow
|
||||
of execution of Rust code are `if` expressions and loops.
|
||||
|
||||
### `if` Expressions
|
||||
|
||||
|
@ -21,15 +20,14 @@ the `if` expression. In the *src/main.rs* file, input the following:
|
|||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-26-if-true/src/main.rs}}
|
||||
```
|
||||
|
||||
All `if` expressions start with the keyword `if`, which is followed by a
|
||||
condition. In this case, the condition checks whether or not the variable
|
||||
`number` has a value less than 5. The block of code we want to execute if the
|
||||
condition is true is placed immediately after the condition inside curly
|
||||
brackets. Blocks of code associated with the conditions in `if` expressions are
|
||||
sometimes called *arms*, just like the arms in `match` expressions that we
|
||||
discussed in the [“Comparing the Guess to the Secret
|
||||
Number”][comparing-the-guess-to-the-secret-number]<!-- ignore --> section of
|
||||
Chapter 2.
|
||||
All `if` expressions start with the keyword `if`, followed by a condition. In
|
||||
this case, the condition checks whether or not the variable `number` has a
|
||||
value less than 5. We place block of code to execute if the condition is true
|
||||
immediately after the condition inside curly brackets. Blocks of code
|
||||
associated with the conditions in `if` expressions are sometimes called *arms*,
|
||||
just like the arms in `match` expressions that we discussed in the [“Comparing
|
||||
the Guess to the Secret Number”][comparing-the-guess-to-the-secret-number]<!--
|
||||
ignore --> section of Chapter 2.
|
||||
|
||||
Optionally, we can also include an `else` expression, which we chose
|
||||
to do here, to give the program an alternative block of code to execute should
|
||||
|
@ -90,7 +88,7 @@ Running this code will print `number was something other than zero`.
|
|||
|
||||
#### Handling Multiple Conditions with `else if`
|
||||
|
||||
You can have multiple conditions by combining `if` and `else` in an `else if`
|
||||
You can use multiple conditions by combining `if` and `else` in an `else if`
|
||||
expression. For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
@ -120,7 +118,7 @@ Rust branching construct called `match` for these cases.
|
|||
#### Using `if` in a `let` Statement
|
||||
|
||||
Because `if` is an expression, we can use it on the right side of a `let`
|
||||
statement, as in Listing 3-2.
|
||||
statement to assign the outcome to a variable, as in Listing 3-2.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -162,18 +160,18 @@ find the problem in the program:
|
|||
|
||||
The expression in the `if` block evaluates to an integer, and the expression in
|
||||
the `else` block evaluates to a string. This won’t work because variables must
|
||||
have a single type. Rust needs to know at compile time what type the `number`
|
||||
variable is, definitively, so it can verify at compile time that its type is
|
||||
valid everywhere we use `number`. Rust wouldn’t be able to do that if the type
|
||||
of `number` was only determined at runtime; the compiler would be more complex
|
||||
and would make fewer guarantees about the code if it had to keep track of
|
||||
multiple hypothetical types for any variable.
|
||||
have a single type, and Rust needs to know at compile time what type the
|
||||
`number` variable is, definitively. Knowing the type of `number` lets the
|
||||
compiler verify the type is valid everywhere we use `number`. Rust wouldn’t be
|
||||
able to do that if the type of `number` was only determined at runtime; the
|
||||
compiler would be more complex and would make fewer guarantees about the code
|
||||
if it had to keep track of multiple hypothetical types for any variable.
|
||||
|
||||
### Repetition with Loops
|
||||
|
||||
It’s often useful to execute a block of code more than once. For this task,
|
||||
Rust provides several *loops*. A loop runs through the code inside the loop
|
||||
body to the end and then starts immediately back at the beginning. To
|
||||
Rust provides several *loops*, which will run through the code inside the loop
|
||||
body to the end and then start immediately back at the beginning. To
|
||||
experiment with loops, let’s make a new project called *loops*.
|
||||
|
||||
Rust has three kinds of loops: `loop`, `while`, and `for`. Let’s try each one.
|
||||
|
@ -193,8 +191,8 @@ like this:
|
|||
```
|
||||
|
||||
When we run this program, we’ll see `again!` printed over and over continuously
|
||||
until we stop the program manually. Most terminals support a keyboard shortcut,
|
||||
<span class="keystroke">ctrl-c</span>, to interrupt a program that is stuck in
|
||||
until we stop the program manually. Most terminals support the keyboard shortcut
|
||||
<span class="keystroke">ctrl-c</span> to interrupt a program that is stuck in
|
||||
a continual loop. Give it a try:
|
||||
|
||||
<!-- manual-regeneration
|
||||
|
@ -220,20 +218,20 @@ The symbol `^C` represents where you pressed <span class="keystroke">ctrl-c
|
|||
depending on where the code was in the loop when it received the interrupt
|
||||
signal.
|
||||
|
||||
Fortunately, Rust provides a way to break out of a loop from code. You can
|
||||
place the `break` keyword within the loop to tell the program when to stop
|
||||
Fortunately, Rust also provides a way to break out of a loop using code. You
|
||||
can place the `break` keyword within the loop to tell the program when to stop
|
||||
executing the loop. Recall that we did this in the guessing game in the
|
||||
[“Quitting After a Correct Guess”][quitting-after-a-correct-guess]<!-- ignore
|
||||
--> section of Chapter 2 to exit the program when the user won the game by
|
||||
guessing the correct number.
|
||||
|
||||
We also used `continue` in the guessing game. The `continue` keyword within a
|
||||
loop tells the program to skip over any remaining code in this iteration of the
|
||||
loop and go to the next iteration.
|
||||
We also used `continue` in the guessing game, which in a loop tells the program
|
||||
to skip over any remaining code in this iteration of the loop and go to the
|
||||
next iteration.
|
||||
|
||||
If you have loops within loops, `break` and `continue` apply to the innermost
|
||||
loop at that point. You can optionally specify a *loop label* on a loop and
|
||||
then use the label with `break` or `continue` to have those keywords applied to
|
||||
loop at that point. You can optionally specify a *loop label* on a loop that we
|
||||
can then use with `break` or `continue` to specify that those keywords apply to
|
||||
the labeled loop instead of the innermost loop. Here’s an example with two
|
||||
nested loops:
|
||||
|
||||
|
@ -253,11 +251,11 @@ doesn’t specify a label will exit the inner loop only. The `break
|
|||
#### Returning Values from Loops
|
||||
|
||||
One of the uses of a `loop` is to retry an operation you know might fail, such
|
||||
as checking whether a thread has completed its job. However, you might need to
|
||||
pass the result of that operation to the rest of your code. To do this, you can
|
||||
add the value you want returned after the `break` expression you use to stop
|
||||
the loop; that value will be returned out of the loop so you can use it, as
|
||||
shown here:
|
||||
as checking whether a thread has completed its job. You might also need to pass
|
||||
the result of that operation out of the loop to the rest of your code. To do
|
||||
this, you can add the value you want returned after the `break` expression you
|
||||
use to stop the loop; that value will be returned out of the loop so you can
|
||||
use it, as shown here:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-33-return-value-from-loop/src/main.rs}}
|
||||
|
@ -273,16 +271,14 @@ print the value in `result`, which in this case is 20.
|
|||
|
||||
#### Conditional Loops with `while`
|
||||
|
||||
It’s often useful for a program to evaluate a condition within a loop. While
|
||||
the condition is true, the loop runs. When the condition ceases to be true, the
|
||||
program calls `break`, stopping the loop. This loop type could be implemented
|
||||
using a combination of `loop`, `if`, `else`, and `break`; you could try that
|
||||
now in a program, if you’d like.
|
||||
|
||||
However, this pattern is so common that Rust has a built-in language construct
|
||||
for it, called a `while` loop. Listing 3-3 uses `while`: the program loops
|
||||
three times, counting down each time, and then, after the loop, it prints
|
||||
another message and exits.
|
||||
A program will often need to evaluate a condition within a loop. While the
|
||||
condition is true, the loop runs. When the condition ceases to be true, the
|
||||
program calls `break`, stopping the loop. It's possible to implement behavior
|
||||
like this using a combination of `loop`, `if`, `else`, and `break`; you could
|
||||
try that now in a program, if you’d like. However, this pattern is so common
|
||||
that Rust has a built-in language construct for it, called a `while` loop. In
|
||||
Listing 3-3, we use `while` to loop the program three times, counting down each
|
||||
time, and then, after the loop, print a message and exit.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -299,8 +295,9 @@ true, the code runs; otherwise, it exits the loop.
|
|||
|
||||
#### Looping Through a Collection with `for`
|
||||
|
||||
You could use the `while` construct to loop over the elements of a collection,
|
||||
such as an array. For example, let’s look at Listing 3-4.
|
||||
You can choose to use the `while` construct to loop over the elements of a
|
||||
collection, such as an array. For example, the loop in Listing 3-4 prints each
|
||||
element in the array `a`.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -324,9 +321,11 @@ All five array values appear in the terminal, as expected. Even though `index`
|
|||
will reach a value of `5` at some point, the loop stops executing before trying
|
||||
to fetch a sixth value from the array.
|
||||
|
||||
But this approach is error prone; we could cause the program to panic if the
|
||||
index value or test condition are incorrect. It’s also slow, because the
|
||||
compiler adds runtime code to perform the conditional check of whether the
|
||||
However, this approach is error prone; we could cause the program to panic if
|
||||
the index value or test condition are incorrect. For example, if you changed
|
||||
the definition of the `a` array to have four elements but forgot to update the
|
||||
condition to `while index < 4`, the code would panic. It’s also slow, because
|
||||
the compiler adds runtime code to perform the conditional check of whether the
|
||||
index is within the bounds of the array on every iteration through the loop.
|
||||
|
||||
As a more concise alternative, you can use a `for` loop and execute some code
|
||||
|
@ -346,19 +345,17 @@ importantly, we’ve now increased the safety of the code and eliminated the
|
|||
chance of bugs that might result from going beyond the end of the array or not
|
||||
going far enough and missing some items.
|
||||
|
||||
For example, in the code in Listing 3-4, if you changed the definition of the
|
||||
`a` array to have four elements but forgot to update the condition to `while
|
||||
index < 4`, the code would panic. Using the `for` loop, you wouldn’t need to
|
||||
remember to change any other code if you changed the number of values in the
|
||||
array.
|
||||
Using the `for` loop, you wouldn’t need to remember to change any other code if
|
||||
you changed the number of values in the array, as you would with the method
|
||||
used in Listing 3-4.
|
||||
|
||||
The safety and conciseness of `for` loops makes them the most commonly used
|
||||
loop construct in Rust. Even in situations in which you want to run some code
|
||||
a certain number of times, as in the countdown example that used a `while` loop
|
||||
The safety and conciseness of `for` loops make them the most commonly used loop
|
||||
construct in Rust. Even in situations in which you want to run some code a
|
||||
certain number of times, as in the countdown example that used a `while` loop
|
||||
in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that
|
||||
would be to use a `Range`, which is a type provided by the standard library
|
||||
that generates all numbers in sequence starting from one number and ending
|
||||
before another number.
|
||||
would be to use a `Range`, provided by the standard library, which generates
|
||||
all numbers in sequence starting from one number and ending before another
|
||||
number.
|
||||
|
||||
Here’s what the countdown would look like using a `for` loop and another method
|
||||
we’ve not yet talked about, `rev`, to reverse the range:
|
||||
|
@ -374,8 +371,8 @@ This code is a bit nicer, isn’t it?
|
|||
## Summary
|
||||
|
||||
You made it! That was a sizable chapter: you learned about variables, scalar
|
||||
and compound data types, functions, comments, `if` expressions, and loops! If
|
||||
you want to practice with the concepts discussed in this chapter, try building
|
||||
and compound data types, functions, comments, `if` expressions, and loops!
|
||||
To practice with the concepts discussed in this chapter, try building
|
||||
programs to do the following:
|
||||
|
||||
* Convert temperatures between Fahrenheit and Celsius.
|
||||
|
|
Loading…
Reference in New Issue