mirror of
https://github.com/rust-lang/book.git
synced 2026-06-12 14:50:45 -04:00
Taking a snapshot after carol's 2nd page review
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
This chapter covers concepts that appear in almost every programming language
|
||||
and how they work in Rust. Many programming languages have much in common at
|
||||
their core. None of the concepts presented in this chapter are unique to Rust,
|
||||
but we’ll discuss them in the context of Rust and explain their conventions.
|
||||
but we’ll discuss them in the context of Rust and explain its conventions.
|
||||
|
||||
Specifically, you’ll learn about variables, basic types, functions, comments,
|
||||
and control flow. These foundations will be in every Rust program, and learning
|
||||
@@ -14,8 +14,8 @@ them early will give you a strong core to start from.
|
||||
|
||||
> ### Keywords
|
||||
>
|
||||
> The Rust language has a set of *keywords* that have been reserved for use by
|
||||
> the language only, much like other languages do. Keep in mind that you cannot
|
||||
> The Rust language has a set of *keywords* that are reserved for use by
|
||||
> the language only, much as in other languages. Keep in mind that you cannot
|
||||
> use these words as names of variables or functions. Most of the keywords have
|
||||
> special meanings, and you’ll be using them to do various tasks in your Rust
|
||||
> programs; a few have no current functionality associated with them but have
|
||||
@@ -24,18 +24,19 @@ them early will give you a strong core to start from.
|
||||
|
||||
## Variables and Mutability
|
||||
|
||||
As mentioned in Chapter 2, by default variables are *immutable*. This is one of
|
||||
many nudges in Rust that encourages you to write your code in a way that takes
|
||||
advantage of the safety and easy concurrency that Rust offers. However, you
|
||||
still have the option to make your variables mutable. Let’s explore how and why
|
||||
Rust encourages you to favor immutability, and why you might want to opt out.
|
||||
As mentioned in Chapter 2, by default variables are immutable. This is one of
|
||||
many nudges Rust gives you to write your code in a way that takes advantage of
|
||||
the safety and easy concurrency that Rust offers. However, you still have the
|
||||
option to make your variables mutable. Let’s explore how and why Rust
|
||||
encourages you to favor immutability and why sometimes you might want to opt
|
||||
out.
|
||||
|
||||
When a variable is immutable, that means once a value is bound to a name, you
|
||||
can’t change that value. To illustrate, let’s generate a new project called
|
||||
*variables* in your *projects* directory by using `cargo new --bin variables`.
|
||||
When a variable is immutable, once a value is bound to a name, you can’t change
|
||||
that value. To illustrate this, let’s generate a new project called *variables*
|
||||
in your *projects* directory by using `cargo new --bin variables`.
|
||||
|
||||
Then, in your new *variables* directory, open *src/main.rs* and replace its
|
||||
code with the following:
|
||||
code with the following code that won’t compile just yet:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -67,8 +68,8 @@ Even though compiler errors can be frustrating, 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.
|
||||
|
||||
The error indicates that the cause of the error is that we `cannot assign twice
|
||||
to immutable variable x`, because we tried to assign a second value to the
|
||||
The error indicates that the cause of the error is that you `cannot 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
|
||||
@@ -76,21 +77,21 @@ 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.
|
||||
This cause of bugs can be difficult to track down after the fact, especially
|
||||
when the second piece of code changes the value only *sometimes*.
|
||||
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*.
|
||||
|
||||
In Rust the compiler guarantees that when we state that a value won’t change,
|
||||
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, which can
|
||||
make code easier to reason about.
|
||||
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; we
|
||||
can make them mutable by adding `mut` in front of the variable name. In
|
||||
addition to allowing this value to change, it conveys intent to future readers
|
||||
of the code by indicating that other parts of the code will be changing this
|
||||
variable value.
|
||||
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 value.
|
||||
|
||||
For example, change *src/main.rs* to the following:
|
||||
For example, let’s change *src/main.rs* to the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -103,7 +104,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
When we run this program, we get the following:
|
||||
When we run the program now, we get this:
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
@@ -114,44 +115,43 @@ The value of x is: 5
|
||||
The value of x is: 6
|
||||
```
|
||||
|
||||
Using `mut`, we’re allowed to change the value that `x` binds to from `5` to
|
||||
`6`. In some cases, you’ll want to make a variable mutable because it makes the
|
||||
code more convenient to write than an implementation that only uses immutable
|
||||
variables.
|
||||
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.
|
||||
|
||||
There are multiple trade-offs to consider, in addition to the prevention of
|
||||
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 reason about, so the lower
|
||||
a more functional programming style may be easier to think through, so lower
|
||||
performance might be a worthwhile penalty for gaining that clarity.
|
||||
|
||||
### 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 also values that are bound to a name and
|
||||
are not allowed to change, but there are a few differences between constants
|
||||
and variables.
|
||||
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, we aren’t allowed to use `mut` with constants: constants aren’t only
|
||||
immutable by default, they’re always immutable.
|
||||
First, you aren’t allowed to use `mut` with constants. Constants aren’t just
|
||||
immutable by default—they’re always immutable.
|
||||
|
||||
We declare constants using the `const` keyword instead of the `let` keyword,
|
||||
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 we must always annotate the type.
|
||||
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.
|
||||
|
||||
The last difference is that constants may only be set to a constant expression,
|
||||
The last difference is that constants may be set only to a constant expression,
|
||||
not the result of a function call or any other value that could only be
|
||||
computed at runtime.
|
||||
|
||||
Here’s an example of a constant declaration where the constant’s name is
|
||||
`MAX_POINTS` and its value is set to 100,000. (Rust constant naming convention
|
||||
is to use all upper case with underscores between words):
|
||||
`MAX_POINTS` and its value is set to 100,000. (Rust’s constant naming
|
||||
convention is to use all uppercase with underscores between words):
|
||||
|
||||
```
|
||||
const MAX_POINTS: u32 = 100_000;
|
||||
@@ -170,12 +170,13 @@ hardcoded value needed to be updated in the future.
|
||||
|
||||
### Shadowing
|
||||
|
||||
As we saw in the guessing game tutorial in Chapter 2, we can declare a new
|
||||
variable with the same name as a previous variable, and the new variable
|
||||
*shadows* the previous variable. Rustaceans say that the first variable is
|
||||
*shadowed* by the second, which means that the second variable’s value is what
|
||||
we’ll see when we use the variable. 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 “Comparing the Guess to the Secret Number” section in Chapter
|
||||
2, you can declare a new variable with the same name as a previous variable,
|
||||
and the new variable shadows the previous variable. Rustaceans say that the
|
||||
first variable is *shadowed* by the second, which means that the second
|
||||
variable’s value is what appears 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
|
||||
|
||||
@@ -193,9 +194,9 @@ fn main() {
|
||||
|
||||
This program first binds `x` to a value of `5`. Then it shadows `x` by
|
||||
repeating `let x =`, taking the original value and adding `1` so the value of
|
||||
`x` is then `6`. The third `let` statement also shadows `x`, taking the
|
||||
previous value and multiplying it by `2` to give `x` a final value of `12`.
|
||||
When you run this program, it will output the following:
|
||||
`x` is then `6`. The third `let` statement also shadows `x`, multiplying the
|
||||
previous value by `2` to give `x` a final value of `12`. When we run this
|
||||
program, it will output the following:
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
@@ -205,14 +206,15 @@ $ cargo run
|
||||
The value of x is: 12
|
||||
```
|
||||
|
||||
This is different than marking a variable as `mut`, because unless we use the
|
||||
`let` keyword again, we’ll get a compile-time error if we accidentally try to
|
||||
reassign to this variable. We can perform a few transformations on a value but
|
||||
have the variable be immutable after those transformations have been completed.
|
||||
Shadowing is different than marking a variable as `mut`, because we’ll get a
|
||||
compile-time error if we accidentally try to reassign to this variable without
|
||||
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.
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
@@ -221,10 +223,10 @@ let spaces = " ";
|
||||
let spaces = spaces.len();
|
||||
```
|
||||
|
||||
This construct is allowed because the first `spaces` variable is a string type,
|
||||
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, like `spaces_str` and
|
||||
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:
|
||||
|
||||
@@ -251,17 +253,16 @@ can have.
|
||||
|
||||
## Data Types
|
||||
|
||||
Every value in Rust is of a certain *type*, which tells Rust what kind of data
|
||||
is being specified so it knows how to work with that data. In this section,
|
||||
we’ll look at a number of types that are built into the language. We split the
|
||||
types into two subsets: scalar and compound.
|
||||
Every value in Rust is of a certain *data type*, which tells Rust what kind of
|
||||
data is being specified so it knows how to work with that data. We’ll look at
|
||||
two data type subsets: scalar and compound.
|
||||
|
||||
Throughout this section, keep in mind that Rust is a *statically typed*
|
||||
language, which means that it must know the types of all variables at compile
|
||||
time. The compiler can usually infer what type we want to use based on the
|
||||
value and how we use it. In cases when many types are possible, such as when we
|
||||
converted a `String` to a numeric type using `parse` in Chapter 2, we must add
|
||||
a type annotation, like this:
|
||||
Keep in mind that Rust is a *statically typed* language, which means that it
|
||||
must know the types of all variables at compile time. The compiler can usually
|
||||
infer what type we want to use based on the value and how we use it. In cases
|
||||
when many types are possible, such as when we converted a `String` to a numeric
|
||||
type using `parse` in the “Comparing the Guess to the Secret Number” section in
|
||||
Chapter 2, we must add a type annotation, like this:
|
||||
|
||||
```
|
||||
let guess: u32 = "42".parse().expect("Not a number!");
|
||||
@@ -269,7 +270,7 @@ let guess: u32 = "42".parse().expect("Not a number!");
|
||||
|
||||
If we don’t add the type annotation here, Rust will display the following
|
||||
error, which means the compiler needs more information from us to know which
|
||||
possible type we want to use:
|
||||
type we want to use:
|
||||
|
||||
```
|
||||
error[E0282]: type annotations needed
|
||||
@@ -282,38 +283,37 @@ error[E0282]: type annotations needed
|
||||
| consider giving `guess` a type
|
||||
```
|
||||
|
||||
You’ll see different type annotations as we discuss the various data types.
|
||||
You’ll see different type annotations for other data types.
|
||||
|
||||
### Scalar Types
|
||||
|
||||
A *scalar* type represents a single value. Rust has four primary scalar types:
|
||||
integers, floating-point numbers, Booleans, and characters. You’ll likely
|
||||
recognize these from other programming languages, but let’s jump into how they
|
||||
work in Rust.
|
||||
integers, floating-point numbers, Booleans, and characters. You may recognize
|
||||
these from other programming languages. Let’s jump into how they work in Rust.
|
||||
|
||||
#### Integer Types
|
||||
|
||||
An *integer* is a number without a fractional component. We used one integer
|
||||
type earlier in this chapter, 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.
|
||||
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.
|
||||
|
||||
Table 3-1: Integer Types in Rust
|
||||
|
||||
| Length | Signed | Unsigned |
|
||||
|--------|--------|----------|
|
||||
| 8-bit | i8 | u8 |
|
||||
| 16-bit | i16 | u16 |
|
||||
| 32-bit | i32 | u32 |
|
||||
| 64-bit | i64 | u64 |
|
||||
| arch | isize | usize |
|
||||
| Length | Signed | Unsigned |
|
||||
|--------|---------|----------|
|
||||
| 8-bit | `i8` | `u8` |
|
||||
| 16-bit | `i16` | `u16` |
|
||||
| 32-bit | `i32` | `u32` |
|
||||
| 64-bit | `i64` | `u64` |
|
||||
| arch | `isize` | `usize` |
|
||||
|
||||
Each variant can be either signed or unsigned and has an explicit size.
|
||||
Signed and unsigned refers to whether it’s possible for the number to be
|
||||
negative or positive; in other words, whether the number needs to have a sign
|
||||
*Signed* and *unsigned* refer to whether it’s possible for the number to be
|
||||
negative or positive—in other words, whether the number needs to have a sign
|
||||
with it (signed) or whether it will only ever be positive and can therefore be
|
||||
represented without a sign (unsigned). It’s like writing numbers on paper: when
|
||||
the sign matters, a number is shown with a plus sign or a minus sign; however,
|
||||
@@ -323,13 +323,13 @@ unsure what this is, you can search for it online; an explanation is outside
|
||||
the scope of this book).
|
||||
|
||||
Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
|
||||
1</sup> - 1 inclusive, where `n` is the number of bits that variant uses. So an
|
||||
1</sup> - 1 inclusive, where *n* is the number of bits that variant uses. So an
|
||||
`i8` can store numbers from -(2<sup>7</sup>) to 2<sup>7</sup> - 1, which equals
|
||||
-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
|
||||
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.
|
||||
|
||||
You can write integer literals in any of the forms shown in Table 3-2. Note
|
||||
@@ -347,9 +347,9 @@ Table 3-2: Integer Literals in Rust
|
||||
| Byte (`u8` only) | `b'A'` |
|
||||
|
||||
So how do you know which type of integer to use? If you’re unsure, Rust’s
|
||||
defaults are generally good choices, and integer types default to `i32`: it’s
|
||||
generally the fastest, even on 64-bit systems. The primary situation in which
|
||||
you’d use `isize` or `usize` is when indexing some sort of collection.
|
||||
defaults are generally good choices, and integer types default to `i32`: this
|
||||
type is generally the fastest, even on 64-bit systems. The primary situation in
|
||||
which you’d use `isize` or `usize` is when indexing some sort of collection.
|
||||
|
||||
#### Floating-Point Types
|
||||
|
||||
@@ -376,7 +376,7 @@ Floating-point numbers are represented according to the IEEE-754 standard. The
|
||||
|
||||
#### Numeric Operations
|
||||
|
||||
Rust supports the usual basic mathematical operations you’d expect for all of the
|
||||
Rust supports the basic mathematical operations you’d expect for all of the
|
||||
number types: addition, subtraction, multiplication, division, and remainder.
|
||||
The following code shows how you’d use each one in a `let` statement:
|
||||
|
||||
@@ -427,10 +427,10 @@ section.
|
||||
|
||||
#### The Character Type
|
||||
|
||||
So far we’ve only worked with numbers, but Rust supports letters too. Rust’s
|
||||
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 the `char` type is specified with
|
||||
single quotes, as opposed to strings that use double quotes:
|
||||
code shows one way to use it. (Note that the `char` type is specified with
|
||||
single quotes, as opposed to strings, which use double quotes.)
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -443,20 +443,19 @@ fn main() {
|
||||
```
|
||||
|
||||
Rust’s `char` type represents a Unicode Scalar Value, which means it can
|
||||
represent a lot more than just ASCII. Accented letters, Chinese/Japanese/Korean
|
||||
ideographs, emoji, and zero width spaces are all valid `char` types in Rust.
|
||||
Unicode Scalar Values range from `U+0000` to `U+D7FF` and `U+E000` to
|
||||
represent a lot more than just ASCII. Accented letters; Chinese, Japanese, and
|
||||
Korean ideographs; emoji; and zero-width spaces are all valid `char` types 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 the “Strings” section
|
||||
in Chapter 8.
|
||||
`char` is in Rust. We’ll discuss this topic in detail in “Strings” in Chapter 8.
|
||||
|
||||
### Compound Types
|
||||
|
||||
*Compound types* can group multiple values of other types into one type. Rust
|
||||
has two primitive compound types: tuples and arrays.
|
||||
*Compound types* can group multiple values into one type. Rust has two
|
||||
primitive compound types: tuples and arrays.
|
||||
|
||||
#### Grouping Values into Tuples
|
||||
#### The Tuple Type
|
||||
|
||||
A tuple is a general way of grouping together some number of other values with
|
||||
a variety of types into one compound type.
|
||||
@@ -474,7 +473,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
The variable `tup` binds to the entire tuple, since a tuple is considered a
|
||||
The variable `tup` binds to the entire tuple, because a tuple is considered a
|
||||
single compound element. To get the individual values out of a tuple, we can
|
||||
use pattern matching to destructure a tuple value, like this:
|
||||
|
||||
@@ -496,9 +495,9 @@ 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 also access a
|
||||
tuple element directly by using a period (`.`) followed by the index of the
|
||||
value we want to access. For example:
|
||||
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:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -518,11 +517,11 @@ This program creates a tuple, `x`, and then makes new variables for each
|
||||
element by using their index. As with most programming languages, the first
|
||||
index in a tuple is 0.
|
||||
|
||||
#### Arrays
|
||||
#### 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 than arrays in some other languages because arrays in Rust have a
|
||||
different from arrays in some other languages because arrays in Rust have a
|
||||
fixed length: once declared, they cannot grow or shrink in size.
|
||||
|
||||
In Rust, the values going into an array are written as a comma-separated list
|
||||
@@ -538,11 +537,11 @@ 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. They aren’t as
|
||||
flexible as the vector type, though. The vector type 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.
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -556,7 +555,7 @@ let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
|
||||
##### Accessing Array Elements
|
||||
|
||||
An array is a single chunk of memory allocated on the stack. We can access
|
||||
An array is a single chunk of memory allocated on the stack. You can access
|
||||
elements of an array using indexing, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
@@ -605,7 +604,7 @@ thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
```
|
||||
|
||||
The compilation didn’t produce any errors, but the program results in a
|
||||
The compilation didn’t produce any errors, but the program resulted in a
|
||||
*runtime* error and didn’t exit successfully. When you attempt to access an
|
||||
element using indexing, Rust will check that the index you’ve specified is less
|
||||
than the array length. If the index is greater than the length, Rust will
|
||||
@@ -617,7 +616,7 @@ incorrect index, invalid memory can be accessed. Rust protects you against this
|
||||
kind of error by immediately exiting instead of allowing the memory access and
|
||||
continuing. Chapter 9 discusses more of Rust’s error handling.
|
||||
|
||||
## How Functions Work
|
||||
## Functions
|
||||
|
||||
Functions are pervasive in Rust code. You’ve already seen one of the most
|
||||
important functions in the language: the `main` function, which is the entry
|
||||
@@ -673,12 +672,12 @@ called and its message is printed.
|
||||
### Function 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, we 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.
|
||||
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:
|
||||
@@ -732,13 +731,13 @@ fn another_function(x: i32, y: i32) {
|
||||
```
|
||||
|
||||
This example creates a function with two parameters, both of which are `i32`
|
||||
types. The function then prints out the values in both of its parameters. Note
|
||||
that function parameters don’t all need to be the same type, they just happen
|
||||
to be in this example.
|
||||
types. The function then prints the values in both of its parameters. Note that
|
||||
function parameters don’t all need to be the same type, they just happen to be
|
||||
in this example.
|
||||
|
||||
Let’s try running this code. Replace the program currently in your *functions*
|
||||
project’s *src/main.rs* file with the preceding example, and run it using
|
||||
`cargo run`:
|
||||
project’s *src/main.rs* file with the preceding example and run it using `cargo
|
||||
run`:
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
@@ -756,7 +755,7 @@ as the value for `y`, the two strings are printed with these values.
|
||||
|
||||
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 we have seen expressions as parts of statements. Because Rust is an
|
||||
but you have seen an expression as part of statements. 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
|
||||
@@ -779,7 +778,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 3-1: A `main` function declaration containing one statement.
|
||||
Listing 3-1: A `main` function declaration containing one statement
|
||||
|
||||
Function definitions are also statements; the entire preceding example is a
|
||||
statement in itself.
|
||||
@@ -810,15 +809,15 @@ error: expected expression, found statement (`let`)
|
||||
```
|
||||
|
||||
The `let y = 6` statement does not return a value, so there isn’t anything for
|
||||
`x` to bind to. This is different than in other languages, such as C and Ruby,
|
||||
where the assignment returns the value of the assignment. In those languages,
|
||||
you can write `x = y = 6` and have both `x` and `y` have the value `6`; that is
|
||||
not the case in Rust.
|
||||
`x` to bind to. This is different from what happens in other languages, such as
|
||||
C and Ruby, where the assignment returns the value of the assignment. In those
|
||||
languages, you can write `x = y = 6` and have both `x` and `y` have the value
|
||||
`6`; that is not the case in Rust.
|
||||
|
||||
Expressions evaluate to something and make up most of the rest of the code that
|
||||
you’ll write in Rust. Consider a simple 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 that had the statement `let y = 6;`, `6` is an
|
||||
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:
|
||||
@@ -849,10 +848,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, 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. Keep this in
|
||||
mind as you explore function return values and expressions next.
|
||||
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.
|
||||
Keep this in mind as you explore function return values and expressions next.
|
||||
|
||||
### Functions with Return Values
|
||||
|
||||
@@ -903,8 +902,9 @@ let x = 5;
|
||||
|
||||
Second, the `five` function has no parameters and defines the type of the
|
||||
return value, but the body of the function is a lonely `5` with no semicolon
|
||||
because it’s an expression whose value we want to return. Let’s look at another
|
||||
example:
|
||||
because it’s an expression whose value we want to return.
|
||||
|
||||
Let’s look at another example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -920,9 +920,9 @@ fn plus_one(x: i32) -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
Running this code will print `The value of x is: 6`. What happens if we place a
|
||||
Running this code will print `The value of x is: 6`. But if we place a
|
||||
semicolon at the end of the line containing `x + 1`, changing it from an
|
||||
expression to a statement? We’ll get an error:
|
||||
expression to a statement, we’ll get an error.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -1013,15 +1013,15 @@ discuss in Chapter 14.
|
||||
|
||||
## Control Flow
|
||||
|
||||
Deciding whether or not to run some code depending on if a condition is true or
|
||||
deciding to run some code repeatedly while a condition is true are basic
|
||||
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.
|
||||
|
||||
### `if` Expressions
|
||||
|
||||
An `if` expression allows us to branch our code depending on conditions. We
|
||||
An `if` expression allows you to branch your code depending on conditions. You
|
||||
provide a condition and then state, “If this condition is met, run this block
|
||||
of code. If the condition is not met, do not run this block of code.”
|
||||
|
||||
@@ -1051,7 +1051,9 @@ 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” section of
|
||||
Chapter 2. Optionally, we can also include an `else` expression, which we chose
|
||||
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
|
||||
the condition evaluate to false. If you don’t provide an `else` expression and
|
||||
the condition is false, the program will just skip the `if` block and move on
|
||||
@@ -1085,8 +1087,7 @@ condition was false
|
||||
```
|
||||
|
||||
It’s also worth noting that the condition in this code *must* be a `bool`. If
|
||||
the condition isn’t a `bool`, we’ll get an error. For example, try running the
|
||||
following code:
|
||||
the condition isn’t a `bool`, we’ll get an error. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -1114,9 +1115,9 @@ error[E0308]: mismatched types
|
||||
found type `{integer}`
|
||||
```
|
||||
|
||||
The error indicates that Rust expected a `bool` but got an integer. Rust will
|
||||
not automatically try to convert non-Boolean types to a Boolean, unlike
|
||||
languages such as Ruby and JavaScript. You must be explicit and always provide
|
||||
The error indicates that Rust expected a `bool` but got an integer. Unlike
|
||||
languages such as Ruby and JavaScript, Rust will not automatically try to
|
||||
convert non-Boolean types to a Boolean. You must be explicit and always provide
|
||||
`if` with a Boolean as its condition. If we want the `if` code block to run
|
||||
only when a number is not equal to `0`, for example, we can change the `if`
|
||||
expression to the following:
|
||||
@@ -1135,9 +1136,9 @@ fn main() {
|
||||
|
||||
Running this code will print `number was something other than zero`.
|
||||
|
||||
#### Multiple Conditions with `else if`
|
||||
#### Handling Multiple Conditions with `else if`
|
||||
|
||||
We can have multiple conditions by combining `if` and `else` in an `else if`
|
||||
You can have multiple conditions by combining `if` and `else` in an `else if`
|
||||
expression. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
@@ -1172,18 +1173,18 @@ number is divisible by 3
|
||||
When this program executes, it checks each `if` expression in turn and executes
|
||||
the first body for which the condition holds true. Note that even though 6 is
|
||||
divisible by 2, we don’t see the output `number is divisible by 2`, nor do we
|
||||
see the `number is not divisible by 4, 3, or 2` text from the `else` block. The
|
||||
reason is that Rust will only execute the block for the first true condition,
|
||||
and once it finds one, it won’t even check the rest.
|
||||
see the `number is not divisible by 4, 3, or 2` text from the `else` block.
|
||||
That’s because Rust only executes the block for the first true condition, and
|
||||
once it finds one, it doesn’t even check the rest.
|
||||
|
||||
Using too many `else if` expressions can clutter your code, so if you have more
|
||||
than one, you might want to refactor your code. Chapter 6 describes a powerful
|
||||
Rust branching construct called `match` for these cases.
|
||||
|
||||
#### Using `if` in a `let` statement
|
||||
#### Using `if` in a `let` Statement
|
||||
|
||||
Because `if` is an expression, we can use it on the right side of a `let`
|
||||
statement, for instance in Listing 3-2:
|
||||
statement, as in Listing 3-2:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -1200,8 +1201,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 3-2: Assigning the result of an `if` expression
|
||||
to a variable
|
||||
Listing 3-2: Assigning the result of an `if` expression to a variable
|
||||
|
||||
The `number` variable will be bound to a value based on the outcome of the `if`
|
||||
expression. Run this code to see what happens:
|
||||
@@ -1252,7 +1252,7 @@ error[E0308]: if and else have incompatible types
|
||||
6 | | } else {
|
||||
7 | | "six"
|
||||
8 | | };
|
||||
| |_____^ expected integral variable, found reference
|
||||
| |_____^ expected integral variable, found &str
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found type `&str`
|
||||
@@ -1324,15 +1324,15 @@ program when the user won the game by guessing the correct number.
|
||||
#### 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, you
|
||||
call `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.
|
||||
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, and it’s called a `while` loop. The following example uses `while`: the
|
||||
program loops three times, counting down each time. Then, after the loop, it
|
||||
prints another message and exits:
|
||||
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.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -1350,6 +1350,8 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 3-3: Using a `while` loop to run code while a condition holds true
|
||||
|
||||
This construct eliminates a lot of nesting that would be necessary if you used
|
||||
`loop`, `if`, `else`, and `break`, and it’s clearer. While a condition holds
|
||||
true, the code runs; otherwise, it exits the loop.
|
||||
@@ -1357,7 +1359,7 @@ 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-3:
|
||||
such as an array. For example, let’s look at Listing 3-4:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -1374,13 +1376,12 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 3-3: Looping through each element of a collection
|
||||
using a `while` loop
|
||||
Listing 3-4: Looping through each element of a collection using a `while` loop
|
||||
|
||||
Here, the code counts up through the elements in the array. It starts at index
|
||||
`0`, and then loops until it reaches the final index in the array (that is,
|
||||
when `index < 5` is no longer true). Running this code will print out every
|
||||
element in the array:
|
||||
when `index < 5` is no longer true). Running this code will print every element
|
||||
in the array:
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
@@ -1404,7 +1405,7 @@ code to perform the conditional check on every element on every iteration
|
||||
through the loop.
|
||||
|
||||
As a more concise alternative, you can use a `for` loop and execute some code
|
||||
for each item in a collection. A `for` loop looks like this code in Listing 3-4:
|
||||
for each item in a collection. A `for` loop looks like this code in Listing 3-5:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@@ -1418,17 +1419,16 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 3-4: Looping through each element of a collection
|
||||
using a `for` loop
|
||||
Listing 3-5: Looping through each element of a collection using a `for` loop
|
||||
|
||||
When we run this code, we’ll see the same output as in Listing 3-3. More
|
||||
When we run this code, we’ll see the same output as in Listing 3-4. More
|
||||
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-3, if you removed an item from the `a`
|
||||
For example, in the code in Listing 3-4, if you removed an item from the `a`
|
||||
array but forgot to update the condition to `while index < 4`, the code would
|
||||
panic. Using the `for` loop, you don’t need to remember to change any other
|
||||
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.
|
||||
|
||||
The safety and conciseness of `for` loops make them the most commonly used loop
|
||||
@@ -1458,8 +1458,9 @@ This code is a bit nicer, isn’t it?
|
||||
## Summary
|
||||
|
||||
You made it! That was a sizable chapter: you learned about variables, scalar
|
||||
and `if` expressions, and loops! If you want to practice with the concepts
|
||||
discussed in this chapter, try building programs to do the following:
|
||||
and compound data types, functions, comments, `if` expressions, and loops! If
|
||||
you want to practice with the concepts discussed in this chapter, try building
|
||||
programs to do the following:
|
||||
|
||||
* Convert temperatures between Fahrenheit and Celsius.
|
||||
* Generate the nth Fibonacci number.
|
||||
|
||||
Reference in New Issue
Block a user