Edits to nostarch's chapter 3 edits

This commit is contained in:
Carol (Nichols || Goulding) 2021-11-06 21:20:30 -04:00
parent 274fb463e0
commit d281b7b062
No known key found for this signature in database
GPG Key ID: E907EE5A736F87D4
6 changed files with 416 additions and 406 deletions

View File

@ -37,7 +37,8 @@ that value. To illustrate this, lets 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 wont compile just yet, we'll first examine the immutability error:
code with the following code. This code wont 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.
Its 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, its
possible that the first part of the code wont 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 wont change,
it really wont change, soc you dont have to keep track of it yourself. Your code
is thus easier to reason through.
value thats 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, its possible
that the first part of the code wont 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 wont change, it really wont
change, so you dont 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 variables 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 variables value.
For example, lets change *src/main.rs* to the following:
@ -119,25 +121,26 @@ The value of x is: 6
```
Were 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 youre 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 youre 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 arent allowed to use `mut` with constants. Constants arent just
immutable by default—theyre always immutable. You declare constants using the `const` keyword instead of the `let` keyword,
and the type of the value *must* be annotated. Were about to cover types and
type annotations in the next section, “Data Types,” so dont worry about the
details right now. Just know that you must always annotate the type.
immutable by default—theyre always immutable. You declare constants using the
`const` keyword instead of the `let` keyword, and the type of the value *must*
be annotated. Were about to cover types and type annotations in the next
section, “Data Types,” so dont 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 @@ Heres an example of a constant declaration:
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
```
The constants 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).
Rusts 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 thats
easier to understand and verify, rather than setting this constant to the value
10,800. See the Rust References section on constant evaluation at
The constants 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). Rusts 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 thats easier to understand and verify, rather than setting this constant
to the value 10,800. See the Rust References 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 variables value is what
the program sees when the variable is used. We can shadow a variable by using
the same variables 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
variables value is what the program sees when the variable is used. We can
shadow a variable by using the same variables 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 were
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, well 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, well 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 its 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 youre on a 64-bit architecture and 32 bits
if youre 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 youre on a 64-bit architecture and 32 bits if youre 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
>
> Lets 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 youre 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; well discuss panics in more depth in the “Unrecoverable Errors
> with `panic!`” section in
> Chapter 9.
> Lets 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 youre 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; well discuss panics in more depth in the “Unrecoverable Errors with
> `panic!`” section in Chapter 9.
>
> When youre 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. Rusts 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 its 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-->
Heres an example that shows floating-point numbers in action:
@ -471,8 +486,8 @@ Flow” section.
#### The Character Type
Rusts
`char` type is the languages most primitive alphabetic type. Here's one way to use it.
Rusts `char` type is the languages 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. Rusts `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” isnt
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. Well 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. Rusts `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” isnt 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. Well 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 isnt
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 isnt 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 youre unsure whether to use an array or a vector, chances are you should use a
vector. Chapter 8 discusses vectors in more detail.
If youre 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 arrays 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 arrays 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 weve 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 functions 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 functions 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 functions 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 functions 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 Rusts 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 dont have the same distinctions, so lets 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 dont have the same distinctions, so lets 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. Lets look at some examples.
*Statements* are instructions that perform some action and do not return a
value. *Expressions* evaluate to a resulting value. Lets look at some examples.
Weve 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.
Weve 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 @@ youll 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 youve 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 youve 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.
Heres 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 wont 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 wouldnt 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 wouldnt 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. Heres 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 youd 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 youd 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. Its 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. Its 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, weve 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 wouldnt 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 wouldnt 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.
Heres what the countdown would look like using a `for` loop and another method
weve not yet talked about, `rev`, to reverse the range:

View File

@ -13,7 +13,8 @@ that value. To illustrate this, lets 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 wont compile just yet:
code with the following code. This code wont 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
isnt safely doing what you want it to do yet; they do *not* mean that youre
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.
Its 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, its
possible that the first part of the code wont 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 thats 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, its possible
that the first part of the code wont 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 wont change, it really wont
change, so you dont 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 wont change,
it really wont change. That means that when youre reading and writing code,
you dont 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 variables 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 variables value.
For example, lets change *src/main.rs* to the following:
@ -71,32 +70,26 @@ When we run the program now, we get this:
```
Were allowed to change the value that `x` binds to from `5` to `6` when `mut`
is used. In some cases, youll 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 youre 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 youre 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 arent allowed to use `mut` with constants. Constants arent just
immutable by default—theyre always immutable.
You declare constants using the `const` keyword instead of the `let` keyword,
and the type of the value *must* be annotated. Were about to cover types and
type annotations in the next section, [“Data Types,”][data-types]<!-- ignore
--> so dont worry about the details right now. Just know that you must always
annotate the type.
immutable by default—theyre always immutable. You declare constants using the
`const` keyword instead of the `let` keyword, and the type of the value *must*
be annotated. Were about to cover types and type annotations in the next
section, [“Data Types,”][data-types]<!-- ignore --> so dont 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.
Heres an example of a constant declaration where the constants 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):
Heres an example of a constant declaration:
```rust
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
```
Rusts 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 thats
easier to understand and verify, rather than setting this constant to the value
10,800. See the [Rust References section on constant evaluation][const-eval]
for more information on what operations can be used when declaring constants.
The constants 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). Rusts 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 thats easier to understand and verify, rather than setting this constant
to the value 10,800. See the [Rust References 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 variables value is what the program sees when the
variable is used. We can shadow a variable by using the same variables 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
variables value is what the program sees when the variable is used. We can
shadow a variable by using the same variables 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 were
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, well 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, well 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}}

View File

@ -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 its 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 youre on a 64-bit architecture and 32 bits
if youre 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 youre on a 64-bit architecture and 32 bits if youre 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
>
> Lets 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 youre 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; well discuss panics in more depth in the [“Unrecoverable Errors
> with `panic!`”][unrecoverable-errors-with-panic]<!-- ignore --> section in
> Chapter 9.
> Lets 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 youre 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; well discuss panics in more depth in the [“Unrecoverable Errors with
> `panic!`”][unrecoverable-errors-with-panic]<!-- ignore --> section in Chapter
> 9.
>
> When youre 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 overflows 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. Rusts 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 its roughly the same speed as `f32` but is capable of
more precision.
more precision. All floating-point types are signed.
Heres an example that shows floating-point numbers in action:
@ -180,10 +180,8 @@ Flow”][control-flow]<!-- ignore --> section.
#### The Character Type
So far weve worked only with numbers, but Rust supports letters too. Rusts
`char` type is the languages 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.)
Rusts `char` type is the languages 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}}
```
Rusts `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” isnt
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. Well 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. Rusts `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” isnt 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. Well 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 isnt
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 youre 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 isnt 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 youre 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. Its 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 arrays 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 arrays 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 arrays 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 Rusts 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

View File

@ -6,7 +6,7 @@ point of many programs. Youve 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.
Heres a program that contains an example function definition:
<span class="filename">Filename: src/main.rs</span>
@ -15,9 +15,9 @@ Heres 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 weve 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 functions 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 functions 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 functions 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 functions 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 Rusts 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, weve 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 dont have the same distinctions, so lets 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 dont have the same distinctions, so lets look at
what statements and expressions are and how their differences affect the bodies
of functions.
Weve 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. Lets look at some examples.
*Statements* are instructions that perform some action and do not return a
value. *Expressions* evaluate to a resulting value. Lets 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.
Weve 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 @@ youll 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 youve 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 youve 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

View File

@ -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.
Heres a simple comment:

View File

@ -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 wont 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 wouldnt 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 wouldnt 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
Its 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, lets make a new project called *loops*.
Rust has three kinds of loops: `loop`, `while`, and `for`. Lets try each one.
@ -193,8 +191,8 @@ like this:
```
When we run this program, well 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. Heres an example with two
nested loops:
@ -253,11 +251,11 @@ doesnt 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`
Its 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 youd 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 youd 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, lets 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. Its 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. Its 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, weve 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 wouldnt need to
remember to change any other code if you changed the number of values in the
array.
Using the `for` loop, you wouldnt 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.
Heres what the countdown would look like using a `for` loop and another method
weve not yet talked about, `rev`, to reverse the range:
@ -374,8 +371,8 @@ This code is a bit nicer, isnt 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.