2016-08-20 05:13:41 +08:00
|
|
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
# Common Programming Concepts
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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 Rust’s particular syntax and conventions concerning these
|
|
|
|
|
common concepts.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Specifically, you’ll learn about variables, basic types, functions, comments,
|
|
|
|
|
and control flow. These foundations will be in every Rust program, and learning
|
|
|
|
|
them early will give you a strong core to start from.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
PROD: START BOX
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
### 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
|
|
|
|
|
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
|
|
|
|
|
been reserved for functionality that might be added to Rust in the future. You
|
|
|
|
|
can find a list of the keywords in Appendix A.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
PROD: END BOX
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
## Variables and Mutability
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When a variable is immutable, that means mean 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`.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Then, in your new *variables* directory, open *src/main.rs* and replace its
|
|
|
|
|
code with the following:
|
2016-08-20 05:13:41 +08:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = 5;
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
x = 6;
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-04 03:42:25 +08:00
|
|
|
|
Save and run the program using `cargo run`. You should receive an error
|
2016-11-02 04:47:25 +08:00
|
|
|
|
message, as shown in this output:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
2016-11-01 08:27:36 +08:00
|
|
|
|
Compiling variables v0.0.1 (file:///projects/variables)
|
2016-11-02 04:47:25 +08:00
|
|
|
|
error[E0384]: re-assignment of immutable variable `x`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
--> src/main.rs:4:5
|
2016-11-02 04:47:25 +08:00
|
|
|
|
|
|
|
|
|
|
2 | let x = 5;
|
|
|
|
|
| - first assignment to `x`
|
|
|
|
|
3 | println!("The value of x is: {}", x);
|
|
|
|
|
4 | x = 6;
|
|
|
|
|
| ^^^^^ re-assignment of immutable variable
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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 `re-assignment of immutable
|
2016-09-21 22:54:36 +08:00
|
|
|
|
variable`, because we tried to assign a second value to the immutable `x`
|
|
|
|
|
variable.
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
It’s important that we get compile-time errors when we attempt to change a
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
2016-11-01 08:27:36 +08:00
|
|
|
|
possible that the first part of the code won’t do what it was designed to do.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
This cause of bugs can be difficult to track down after the fact, especially
|
2016-11-02 04:47:25 +08:00
|
|
|
|
when the second piece of code changes the value only *sometimes*.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
In Rust the compiler guarantees that when we 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.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
For example, change *src/main.rs* to the following:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut x = 5;
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
x = 6;
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When we run this program, we get the following:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
2016-11-01 08:27:36 +08:00
|
|
|
|
Compiling variables v0.1.0 (file:///projects/variables)
|
|
|
|
|
Running `target/debug/variables`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
The value of x is: 5
|
|
|
|
|
The value of x is: 6
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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, always creating new instances and
|
|
|
|
|
writing in a more functional programming style may be easier to reason about,
|
|
|
|
|
so the lower performance penalty might be worth it to gain that clarity.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
### Shadowing
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
As we saw in the guessing game tutorial in Chapter 2, we can declare new
|
|
|
|
|
variables with the same name as a previous variables, 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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = 5;
|
|
|
|
|
|
|
|
|
|
let x = x + 1;
|
|
|
|
|
|
|
|
|
|
let x = x * 2;
|
|
|
|
|
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
2016-11-01 08:27:36 +08:00
|
|
|
|
Compiling variables v0.1.0 (file:///projects/variables)
|
|
|
|
|
Running `target/debug/variables`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
The value of x is: 12
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This is different than marking a variable as `mut`, because unless we use the
|
2016-11-01 08:27:36 +08:00
|
|
|
|
`let` keyword again, we’ll get a compile-time error if we accidentally try to
|
2016-11-02 04:47:25 +08:00
|
|
|
|
reassign to this variable. We can perform a few transformations on a value but
|
2016-11-01 08:27:36 +08:00
|
|
|
|
have the variable be immutable after those transformations have been completed.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The other difference between `mut` and shadowing is that because we’re
|
2016-11-01 08:27:36 +08:00
|
|
|
|
effectively creating a new variable when we use the `let` keyword again, we can
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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:
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let spaces = " ";
|
|
|
|
|
let spaces = spaces.len();
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
2016-11-04 03:42:25 +08:00
|
|
|
|
`spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
|
2016-11-02 04:47:25 +08:00
|
|
|
|
try to use `mut` for this, as shown here:
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
let mut spaces = " ";
|
|
|
|
|
spaces = spaces.len();
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
we’ll get a compile-time error because we’re not allowed to mutate a variable’s
|
|
|
|
|
type:
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
2016-11-02 04:47:25 +08:00
|
|
|
|
error[E0308]: mismatched types
|
|
|
|
|
--> src/main.rs:3:14
|
|
|
|
|
|
|
|
|
|
|
3 | spaces = spaces.len();
|
|
|
|
|
| ^^^^^^^^^^^^ expected &str, found usize
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `&str`
|
|
|
|
|
= note: found type `usize`
|
2016-09-21 22:54:36 +08:00
|
|
|
|
```
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Now that we’ve explored how variables work, let’s look at more data types they
|
|
|
|
|
can have.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
## Data Types
|
|
|
|
|
|
|
|
|
|
Every value in Rust is of a certain *type*, which tells Rust what kind of data
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Throughout this section, keep in mind that Rust is a *statically typed*
|
2016-11-01 08:27:36 +08:00
|
|
|
|
language, which means that it must know the types of all variables at compile
|
2016-08-03 10:07:25 +08:00
|
|
|
|
time. The compiler can usually infer what type we want to use based on the
|
2016-09-21 22:54:36 +08:00
|
|
|
|
value and how we use it. In cases when many types are possible, such as when we
|
2016-11-02 04:47:25 +08:00
|
|
|
|
converted a `String` to a numeric type using `parse` in Chapter 2, we must add
|
|
|
|
|
a type annotation, like this:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
```rust
|
|
|
|
|
let guess: u32 = "42".parse().unwrap();
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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:
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
2016-11-02 04:47:25 +08:00
|
|
|
|
error[E0282]: unable to infer enough type information about `_`
|
|
|
|
|
--> src/main.rs:2:5
|
|
|
|
|
|
|
|
|
|
|
2 | let guess = "42".parse().unwrap();
|
|
|
|
|
| ^^^^^ cannot infer type for `_`
|
|
|
|
|
|
|
|
|
|
|
= note: type annotations or generic parameter binding required
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
You’ll see different type annotations as we discuss the various data types.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
### Scalar Types
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
#### Integer Types
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
An *integer* is a number without a fractional component. We used one integer
|
|
|
|
|
type earlier in this chapter, the `i32` type. This type declaration indicates
|
2016-11-01 08:27:36 +08:00
|
|
|
|
that the value it’s associated with should be a signed integer (hence the `i`,
|
2016-11-02 04:47:25 +08:00
|
|
|
|
as opposed to a `u` for unsigned) for a 32-bit system. Table 3-1 shows the
|
|
|
|
|
built-in integer types in Rust. Each variant in the Signed and Unsigned columns
|
|
|
|
|
(for example, *i32*) can be used to declare the type of an integer value.
|
|
|
|
|
|
|
|
|
|
<caption>
|
|
|
|
|
Table 3-1: Integer Types in Rust
|
|
|
|
|
</caption>
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-04 03:42:25 +08:00
|
|
|
|
| Length | Signed | Unsigned |
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|--------|--------|----------|
|
|
|
|
|
| 8-bit | i8 | u8 |
|
|
|
|
|
| 16-bit | i16 | u16 |
|
|
|
|
|
| 32-bit | i32 | u32 |
|
|
|
|
|
| 64-bit | i64 | u64 |
|
|
|
|
|
| arch | isize | usize |
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
|
|
|
|
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,
|
|
|
|
|
when it’s safe to assume the number is positive, it’s shown with no sign.
|
|
|
|
|
Signed numbers are stored using two’s complement representation (if you’re
|
|
|
|
|
unsure what this is, you can search for it online; an explanation is outside
|
|
|
|
|
the scope of this book).
|
|
|
|
|
|
2016-11-04 03:42:25 +08:00
|
|
|
|
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
|
|
|
|
|
`i8` can store numbers from -(2<sup>7</sup>) to 2<sup>7</sup>, 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.
|
2016-11-02 04:47:25 +08:00
|
|
|
|
|
|
|
|
|
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
|
2016-08-03 10:07:25 +08:00
|
|
|
|
if you’re on a 32-bit architecture.
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
You can write integer literals in any of the forms shown in Table 3-2. Note
|
|
|
|
|
that all number literals except the byte literal allow a type suffix, such as
|
2016-09-21 22:54:36 +08:00
|
|
|
|
`57u8`, and `_` as a visual separator, such as `1_000`.
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
<caption>
|
|
|
|
|
Table 3-2: Integer Literals in Rust
|
|
|
|
|
</caption>
|
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
| Number literals | Example |
|
|
|
|
|
|------------------|---------------|
|
|
|
|
|
| Decimal | `98_222` |
|
|
|
|
|
| Hex | `0xff` |
|
|
|
|
|
| Octal | `0o77` |
|
|
|
|
|
| Binary | `0b1111_0000` |
|
|
|
|
|
| Byte (`u8` only) | `b'A'` |
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
So how do you know which type of integer to use? If you’re unsure, Rust’s
|
2016-08-03 10:07:25 +08:00
|
|
|
|
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
|
2016-11-01 08:27:36 +08:00
|
|
|
|
you’d use `isize` or `usize` is when indexing some sort of collection.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
#### Floating-Point Types
|
|
|
|
|
|
|
|
|
|
Rust also has two primitive types for *floating-point numbers*, which are
|
2016-11-01 08:27:36 +08:00
|
|
|
|
numbers with decimal points. Rust’s floating-point types are `f32` and `f64`,
|
2016-11-02 04:47:25 +08:00
|
|
|
|
which are 32 bits and 64 bits in size, respectively. The default type is `f64`
|
|
|
|
|
because it’s roughly the same speed as `f32` but is capable of more precision.
|
|
|
|
|
It’s possible to use an `f64` type on 32-bit systems, but it will be slower
|
|
|
|
|
than using an `f32` type on those systems. Most of the time, trading potential
|
|
|
|
|
worse performance for better precision is a reasonable initial choice, and you
|
|
|
|
|
should benchmark your code if you suspect floating-point size is a problem in
|
|
|
|
|
your situation.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Here’s an example that shows floating-point numbers in action:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = 2.0; // f64
|
|
|
|
|
|
|
|
|
|
let y: f32 = 3.0; // f32
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Floating-point numbers are represented according to the IEEE-754 standard. The
|
2016-11-02 04:47:25 +08:00
|
|
|
|
`f32` type is a single-precision float, and `f64` has double precision.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
#### Numeric Operations
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Rust supports the usual basic mathematic 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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
// addition
|
|
|
|
|
let sum = 5 + 10;
|
|
|
|
|
|
|
|
|
|
// subtraction
|
|
|
|
|
let difference = 95.5 - 4.3;
|
|
|
|
|
|
|
|
|
|
// multiplication
|
|
|
|
|
let product = 4 * 30;
|
|
|
|
|
|
|
|
|
|
// division
|
|
|
|
|
let quotient = 56.7 / 32.2;
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
// remainder
|
2016-08-03 10:07:25 +08:00
|
|
|
|
let remainder = 43 % 5;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Each expression in these statements uses a mathematical operator and evaluates
|
2016-11-02 04:47:25 +08:00
|
|
|
|
to a single value, which is then bound to a variable. Appendix B contains a
|
2016-11-01 08:27:36 +08:00
|
|
|
|
list of all operators that Rust provides.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
#### The Boolean Type
|
|
|
|
|
|
|
|
|
|
As in most other programming languages, a boolean type in Rust has two possible
|
2016-11-02 04:47:25 +08:00
|
|
|
|
values: `true` and `false`. The boolean type in Rust is specified using `bool`.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
For example:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let t = true;
|
|
|
|
|
|
|
|
|
|
let f: bool = false; // with explicit type annotation
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The main way to consume boolean values is through conditionals, such as an `if`
|
2016-11-01 08:27:36 +08:00
|
|
|
|
statement. We’ll cover how `if` statements work in Rust in the “Control Flow”
|
2016-11-02 04:47:25 +08:00
|
|
|
|
section.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
#### The Character Type
|
|
|
|
|
|
|
|
|
|
So far we’ve only worked with numbers, but Rust supports letters too. Rust’s
|
2016-11-02 04:47:25 +08:00
|
|
|
|
`char` type is the language’s most primitive alphabetic type, and the following
|
|
|
|
|
code shows one way to use it:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let c = 'z';
|
|
|
|
|
let z = 'ℤ';
|
|
|
|
|
let heart_eyed_cat = '😻';
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Rust’s `char` type represents a Unicode Scalar Value, which means it can
|
2016-08-03 10:07:25 +08:00
|
|
|
|
represent a lot more than just ASCII. Accented letters, Chinese/Japanese/Korean
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-20 05:13:41 +08:00
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
### Compound Types
|
|
|
|
|
|
|
|
|
|
*Compound types* can group multiple values of other types into one type. Rust
|
|
|
|
|
has two primitive compound types: tuples and arrays.
|
|
|
|
|
|
|
|
|
|
#### Grouping Values into Tuples
|
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
A tuple is a general way of grouping together some number of other values with
|
2016-11-01 22:09:19 +08:00
|
|
|
|
a variety of types into one compound type.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
We create a tuple by writing a comma-separated list of values inside
|
2016-11-01 22:09:19 +08:00
|
|
|
|
parentheses. Each position in the tuple has a type, and the types of the
|
|
|
|
|
different values in the tuple don’t have to be the same. We’ve added optional
|
|
|
|
|
type annotations in this example:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let tup: (i32, f64, u8) = (500, 6.4, 1);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
The variable `tup` binds to the entire tuple, since 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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
2016-09-21 22:54:36 +08:00
|
|
|
|
let tup = (500, 6.4, 1);
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
let (x, y, z) = tup;
|
|
|
|
|
|
|
|
|
|
println!("The value of y is: {}", y);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This program first creates a tuple and binds it to the variable `tup`. It then
|
|
|
|
|
uses a pattern with `let` to take `tup` and turn it into three separate
|
2016-11-01 08:27:36 +08:00
|
|
|
|
variables, `x`, `y`, and `z`. This is called *destructuring*, because it breaks
|
2016-11-02 04:47:25 +08:00
|
|
|
|
the single tuple into three parts. Finally, the program prints the value of
|
|
|
|
|
`y`, which is `6.4`.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let x: (i32, f64, u8) = (500, 6.4, 1);
|
|
|
|
|
|
|
|
|
|
let five_hundred = x.0;
|
|
|
|
|
|
|
|
|
|
let six_point_four = x.1;
|
|
|
|
|
|
|
|
|
|
let one = x.2;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
#### Arrays
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
|
|
|
|
fixed length: once declared, they cannot grow or shrink in size.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
In Rust, the values going into an array are written as a comma-separated list
|
2016-08-03 10:07:25 +08:00
|
|
|
|
inside square brackets:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = [1, 2, 3, 4, 5];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-04 03:42:25 +08:00
|
|
|
|
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.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
An example of when you might want to use an array rather than a vector is in a
|
|
|
|
|
program that needs to know the names of the months of the year. It’s very
|
|
|
|
|
unlikely that such a program will need to add or remove months, so you can use
|
|
|
|
|
an array because you know it will always contain 12 items:
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let months = ["January", "February", "March", "April", "May", "June", "July",
|
|
|
|
|
"August", "September", "October", "November", "December"];
|
|
|
|
|
```
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-04 03:53:50 +08:00
|
|
|
|
##### Accessing Array Elements
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
An array is a single chunk of memory allocated on the stack. We can access
|
2016-08-03 10:07:25 +08:00
|
|
|
|
elements of an array using indexing, like this:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = [1, 2, 3, 4, 5];
|
|
|
|
|
|
|
|
|
|
let first = a[0];
|
|
|
|
|
let second = a[1];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
In this example, the variable named `first` will get the value `1`, because
|
|
|
|
|
that is the value at index `[0]` in the array. The variable named `second` will
|
|
|
|
|
get the value `2` from index `[1]` in the array.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-04 03:53:50 +08:00
|
|
|
|
##### Invalid Array Element Access
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
What happens if we try to access an element of an array that is past the end of
|
|
|
|
|
the array? Say we change the example to the following:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = [1, 2, 3, 4, 5];
|
|
|
|
|
|
|
|
|
|
let element = a[10];
|
|
|
|
|
|
|
|
|
|
println!("The value of element is: {}", element);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Running this code using `cargo run` produces the following result:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling arrays v0.1.0 (file:///projects/arrays)
|
|
|
|
|
Running `target/debug/arrays`
|
2016-09-21 22:54:36 +08:00
|
|
|
|
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
|
2016-11-02 04:47:25 +08:00
|
|
|
|
10', src/main.rs:4
|
2016-08-03 10:07:25 +08:00
|
|
|
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
|
|
|
error: Process didn't exit successfully: `target/debug/arrays` (exit code: 101)
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The compilation didn’t produce any errors, but the program results 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
|
|
|
|
|
*panic*, which is the term Rust uses when a program exits with an error.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This is the first example of Rust’s safety principles in action. In many
|
2016-08-03 10:07:25 +08:00
|
|
|
|
low-level languages, this kind of check is not done, and when you provide an
|
2016-11-02 04:47:25 +08:00
|
|
|
|
incorrect index, invalid memory can be accessed. Rust protects you against this
|
2016-08-03 10:07:25 +08:00
|
|
|
|
kind of error by immediately exiting instead of allowing the memory access and
|
2016-11-02 04:47:25 +08:00
|
|
|
|
continuing. Chapter 9 discusses more of Rust’s error handling.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
## How Functions Work
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
|
|
|
|
point of many programs. You’ve also seen the `fn` keyword, which allows you to
|
2016-08-03 10:07:25 +08:00
|
|
|
|
declare new functions.
|
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
Rust code uses *snake case* as the conventional style for function and variable
|
2016-11-02 04:47:25 +08:00
|
|
|
|
names. In snake case, all letters are lowercase and underscores separate words.
|
|
|
|
|
Here’s a program that contains an example function definition:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
println!("Hello, world!");
|
|
|
|
|
|
|
|
|
|
another_function();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn another_function() {
|
|
|
|
|
println!("Another function.");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Function definitions in Rust start with `fn` and have a set of parentheses
|
|
|
|
|
after the function name. The curly braces tell the compiler where the function
|
|
|
|
|
body begins and ends.
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
We can call any function we’ve defined by entering its name followed by a set
|
|
|
|
|
of parentheses. Because `another_function` is defined in the program, it can be
|
|
|
|
|
called from inside the `main` function. Note that we defined `another_function`
|
|
|
|
|
*after* the `main` function in the source code; we could have defined it before
|
|
|
|
|
as well. Rust doesn’t care where you define your functions, only that they’re
|
|
|
|
|
defined somewhere.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Let’s start a new binary project named *functions* to explore functions
|
|
|
|
|
further. Place the `another_function` example in *src/main.rs* and run it. You
|
|
|
|
|
should see the following output:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling functions v0.1.0 (file:///projects/functions)
|
|
|
|
|
Running `target/debug/functions`
|
|
|
|
|
Hello, world!
|
|
|
|
|
Another function.
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
### Function Arguments
|
|
|
|
|
|
|
|
|
|
Functions can also take arguments. The following rewritten version of
|
2016-09-21 22:54:36 +08:00
|
|
|
|
`another_function` shows what arguments look like in Rust:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
another_function(5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn another_function(x: i32) {
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Try running this program; you should get the following output:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling functions v0.1.0 (file:///projects/functions)
|
|
|
|
|
Running `target/debug/functions`
|
|
|
|
|
The value of x is: 5
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The declaration of `another_function` has one argument named `x`. The type of
|
|
|
|
|
`x` is specified as `i32`. When `5` is passed to `another_function`, the
|
|
|
|
|
`println!` macro puts `5` where the pair of curly braces were in the format
|
2016-09-21 22:54:36 +08:00
|
|
|
|
string.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
In function signatures, you *must* declare the type. This is a deliberate
|
|
|
|
|
decision in Rust’s design: requiring type annotations in function definitions
|
|
|
|
|
means the compiler almost never needs you to use them elsewhere in the code to
|
|
|
|
|
figure out what you mean.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When you want a function to have multiple arguments, separate them inside the
|
|
|
|
|
function signature with commas, like this:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
another_function(5, 6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn another_function(x: i32, y: i32) {
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
println!("The value of y is: {}", y);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This example creates a function with two arguments, both of which are `i32`
|
|
|
|
|
types. If your function has multiple arguments, the arguments don’t need to be
|
|
|
|
|
the same type, but they just happen to be in this example. The function then
|
|
|
|
|
prints out the values of both of its arguments.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Let’s try running this code. Replace the program currently in your *function*
|
|
|
|
|
project’s *src/main.rs* file with the preceding example, and run it using
|
|
|
|
|
`cargo run`:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling functions v0.1.0 (file:///projects/functions)
|
|
|
|
|
Running `target/debug/functions`
|
|
|
|
|
The value of x is: 5
|
|
|
|
|
The value of y is: 6
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Because `5` is passed as the `x` argument and `6` is passed as the `y`
|
|
|
|
|
argument, the two strings are printed with these values.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
### Function Bodies
|
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
Function bodies are made up of a series of statements optionally ending in an
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
2016-08-03 10:07:25 +08:00
|
|
|
|
expression-based language, this is an important distinction to understand.
|
2016-11-01 08:27:36 +08:00
|
|
|
|
Other languages don’t have the same distinctions, so let’s look at what
|
2016-08-03 10:07:25 +08:00
|
|
|
|
statements and expressions are and how their differences affect the bodies of
|
|
|
|
|
functions.
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
### Statements and Expressions
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
We’ve actually already used statements and expressions. *Statements* are
|
2016-08-03 10:07:25 +08:00
|
|
|
|
instructions that perform some action and do not return a value. *Expressions*
|
2016-11-01 08:27:36 +08:00
|
|
|
|
evaluate to a resulting value. Let’s look at some examples.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Creating a variable and assigning a value to it with the `let` keyword is a
|
|
|
|
|
statement. In Listing 3-3, `let y = 6;` is a statement:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let y = 6;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
<caption>
|
|
|
|
|
Listing 3-3: A `main` function declaration containing one statement.
|
|
|
|
|
</caption>
|
|
|
|
|
|
|
|
|
|
Function definitions are also statements; the entire preceding example is a
|
2016-09-21 22:54:36 +08:00
|
|
|
|
statement in itself.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Statements do not return values. Therefore, you can’t assign a `let` statement
|
|
|
|
|
to another variable, as the following code tries to do:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = (let y = 6);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When you run this program, you’ll get an error like this:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling functions v0.1.0 (file:///projects/functions)
|
|
|
|
|
error: expected expression, found statement (`let`)
|
|
|
|
|
--> src/main.rs:2:14
|
2016-11-02 04:47:25 +08:00
|
|
|
|
|
|
|
|
|
|
2 | let x = (let y = 6);
|
|
|
|
|
| ^^^
|
|
|
|
|
|
|
|
|
|
|
= note: variable declaration using `let` is a statement
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
The `let y = 6` statement does not return a value, so there isn’t anything for
|
2016-11-02 04:47:25 +08:00
|
|
|
|
`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.
|
|
|
|
|
|
|
|
|
|
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-3 that had the statement `let y = 6;`, `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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = 5;
|
|
|
|
|
|
|
|
|
|
let y = {
|
|
|
|
|
let x = 3;
|
|
|
|
|
x + 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
println!("The value of y is: {}", y);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This expression:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
{
|
|
|
|
|
let x = 3;
|
|
|
|
|
x + 1
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
|
|
|
|
|
as part of the `let` statement. Note the 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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
### Functions with Return Values
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Functions can return values to the code that calls them. We don’t name return
|
|
|
|
|
values, but we do declare their type after an arrow (`->`). In Rust, the return
|
|
|
|
|
value of the function is synonymous with the value of the final expression in
|
|
|
|
|
the block of the body of a function. Here’s an example of a function that
|
|
|
|
|
returns a value:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn five() -> i32 {
|
|
|
|
|
5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = five();
|
|
|
|
|
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
There are no function calls, macros, or even `let` statements in the `five`
|
2016-11-02 04:47:25 +08:00
|
|
|
|
function—just the number `5` by itself. That’s a perfectly valid function in
|
|
|
|
|
Rust. Note that the function’s return type is specified, too, as `-> i32`. Try
|
|
|
|
|
running this code; the output should look like this:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling functions v0.1.0 (file:///projects/functions)
|
|
|
|
|
Running `target/debug/functions`
|
|
|
|
|
The value of x is: 5
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
The `5` in `five` is the function’s return value, which is why the return type
|
2016-11-02 04:47:25 +08:00
|
|
|
|
is `i32`. Let’s examine this in more detail. There are two important bits:
|
|
|
|
|
first, the line `let x = five();` shows that we’re using the return value of a
|
|
|
|
|
function to initialize a variable. Because the function `five` returns a `5`,
|
|
|
|
|
that line is the same as the following:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let x = 5;
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Second, the `five` function requires no arguments 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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = plus_one(5);
|
|
|
|
|
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn plus_one(x: i32) -> i32 {
|
|
|
|
|
x + 1
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Running this code will print `The value of x is: 6`. What happens if we place a
|
2016-08-03 10:07:25 +08:00
|
|
|
|
semicolon at the end of the line containing `x + 1`, changing it from an
|
|
|
|
|
expression to a statement?
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = plus_one(5);
|
|
|
|
|
|
|
|
|
|
println!("The value of x is: {}", x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn plus_one(x: i32) -> i32 {
|
|
|
|
|
x + 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Running this code produces an error, as follows:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
2016-11-02 04:47:25 +08:00
|
|
|
|
error[E0269]: not all control paths return a value
|
2016-08-03 10:07:25 +08:00
|
|
|
|
--> src/main.rs:7:1
|
2016-11-02 04:47:25 +08:00
|
|
|
|
|
|
|
|
|
|
7 | fn plus_one(x: i32) -> i32 {
|
|
|
|
|
| ^
|
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
help: consider removing this semicolon:
|
|
|
|
|
--> src/main.rs:8:10
|
2016-11-02 04:47:25 +08:00
|
|
|
|
|
|
|
|
|
|
8 | x + 1;
|
|
|
|
|
| ^
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The main error message, “not all control paths return a value,” reveals the
|
|
|
|
|
core issue with this code. The definition of the function `plus_one` says that
|
|
|
|
|
it will return an `i32`, but statements don’t evaluate to a value. Therefore,
|
|
|
|
|
nothing is returned, which contradicts the function definition and results in
|
|
|
|
|
an error. In this output, Rust provides a message to possibly help rectify this
|
|
|
|
|
issue: it suggests removing the semicolon, which would fix the error.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
## Comments
|
|
|
|
|
|
|
|
|
|
All programmers strive to make their code easy to understand, but sometimes
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
Here’s a simple comment:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
// Hello, world.
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
In Rust, comments must start with two slashes and continue until the end of the
|
|
|
|
|
line. For comments that extend beyond a single line, you’ll need to include
|
|
|
|
|
`//` on each line, like this:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
// So we’re doing something complicated here, long enough that we need
|
|
|
|
|
// multiple lines of comments to do it! Whew! Hopefully, this comment will
|
|
|
|
|
// explain what’s going on.
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Comments can also be placed at the end of lines containing code:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let lucky_number = 7; // I’m feeling lucky today.
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
But you’ll more often see them used in this format, with the comment on a
|
|
|
|
|
separate line above the code it's annotating:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
// I’m feeling lucky today.
|
|
|
|
|
let lucky_number = 7;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
That’s all there is to comments. They’re not particularly complicated.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
## Control Flow
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
### `if` Expressions
|
|
|
|
|
|
|
|
|
|
An `if` expression allows us to branch our code depending on conditions. We
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.”
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Create a new project called *branches* in your *projects* directory to explore
|
|
|
|
|
the `if` expression. In the *src/main.rs* file, input the following:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let number = 3;
|
|
|
|
|
|
|
|
|
|
if number < 5 {
|
|
|
|
|
println!("condition was true");
|
|
|
|
|
} else {
|
|
|
|
|
println!("condition was false");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
All `if` expressions start with the keyword `if`, which is followed by a
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
|
|
|
|
braces. 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
|
2016-11-04 03:42:25 +08:00
|
|
|
|
2. Optionally, we can also include an `else` expression, which we chose to do
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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 to
|
|
|
|
|
the next bit of code.
|
|
|
|
|
|
|
|
|
|
Try running this code; you should see the following output:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
|
Running `target/debug/branches`
|
|
|
|
|
condition was true
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Let’s try changing the value of `number` to a value that makes the condition
|
|
|
|
|
`false` to see what happens:
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
let number = 7;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Run the program again, and look at the output:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
|
Running `target/debug/branches`
|
|
|
|
|
condition was false
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
It’s also worth noting that the condition in this code *must* be a `bool`. To
|
|
|
|
|
see what happens if the condition isn’t a `bool`, try running the following
|
|
|
|
|
code:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
let number = 3;
|
|
|
|
|
|
|
|
|
|
if number {
|
|
|
|
|
println!("number was three");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The `if` condition evaluates to a value of `3` this time, and Rust throws an
|
|
|
|
|
error:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
2016-11-02 04:47:25 +08:00
|
|
|
|
error[E0308]: mismatched types
|
2016-08-03 10:07:25 +08:00
|
|
|
|
--> src/main.rs:4:8
|
2016-11-02 04:47:25 +08:00
|
|
|
|
|
|
|
|
|
|
4 | if number {
|
|
|
|
|
| ^^^^^^ expected bool, found integral variable
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `bool`
|
|
|
|
|
= note: found type `{integer}`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
error: aborting due to previous error
|
|
|
|
|
Could not compile `branches`.
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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
|
|
|
|
|
`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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let number = 3;
|
|
|
|
|
|
|
|
|
|
if number != 0 {
|
|
|
|
|
println!("number was something other than zero");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Running this code will print `number was something other than zero`.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-06 01:30:59 +08:00
|
|
|
|
#### Multiple Conditions with `else if`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
We can have multiple conditions by combining `if` and `else` in an `else if`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
expression. For example:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
2016-09-21 22:54:36 +08:00
|
|
|
|
let number = 6;
|
|
|
|
|
|
|
|
|
|
if number % 4 == 0 {
|
|
|
|
|
println!("number is divisible by 4");
|
|
|
|
|
} else if number % 3 == 0 {
|
|
|
|
|
println!("number is divisible by 3");
|
|
|
|
|
} else if number % 2 == 0 {
|
|
|
|
|
println!("number is divisible by 2");
|
2016-08-03 10:07:25 +08:00
|
|
|
|
} else {
|
2016-09-21 22:54:36 +08:00
|
|
|
|
println!("number is not divisible by 4, 3, or 2");
|
2016-08-03 10:07:25 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This program has four possible paths it can take. After running it, you should
|
|
|
|
|
see the following output:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
|
Running `target/debug/branches`
|
2016-09-21 22:54:36 +08:00
|
|
|
|
number is divisible by 3
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-06 01:30:59 +08:00
|
|
|
|
#### Using `if` in a `let` statement
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Because `if` is an expression, we can use it on the right side of a `let`
|
|
|
|
|
statement, for instance in Listing 3-4:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let condition = true;
|
|
|
|
|
let number = if condition {
|
|
|
|
|
5
|
|
|
|
|
} else {
|
|
|
|
|
6
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
println!("The value of number is: {}", number);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
<caption>
|
|
|
|
|
Listing 3-4: Assigning the result of an `if` expression to a variable
|
|
|
|
|
</caption>
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
The `number` variable will be bound to a value based on the outcome of the `if`
|
2016-11-02 04:47:25 +08:00
|
|
|
|
expression. Run this code to see what happens:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
|
Running `target/debug/branches`
|
|
|
|
|
The value of number is: 5
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Remember that blocks of code evaluate to the last expression in them, and
|
|
|
|
|
numbers by themselves are also expressions. In this case, the value of the
|
|
|
|
|
whole `if` expression depends on which block of code executes. This means the
|
|
|
|
|
values that have the potential to be results from each arm of the `if` must be
|
|
|
|
|
the same type; in Listing 3-4, the results of both the `if` arm and the `else`
|
|
|
|
|
arm were `i32` integers. But what happens if the types are mismatched, as in
|
|
|
|
|
the following example?
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
let condition = true;
|
|
|
|
|
|
|
|
|
|
let number = if condition {
|
|
|
|
|
5
|
|
|
|
|
} else {
|
|
|
|
|
"six"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
println!("The value of number is: {}", number);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When we run this code, we’ll get an error. The `if` and `else` arms have value
|
|
|
|
|
types that are incompatible, and Rust indicates exactly where to find the
|
|
|
|
|
problem in the program:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
2016-09-21 22:54:36 +08:00
|
|
|
|
error[E0308]: if and else have incompatible types
|
|
|
|
|
--> src/main.rs:4:18
|
|
|
|
|
|
|
|
|
|
|
4 | let number = if condition {
|
|
|
|
|
| ^ expected integral variable, found reference
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `{integer}`
|
2016-11-02 04:47:25 +08:00
|
|
|
|
= note: found type `&’static str`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The expression in the `if` block evaluates to an integer, and the expression in
|
|
|
|
|
the `else` block evaluates to a string. This won’t work because variables must
|
|
|
|
|
have a single type. Rust needs to know at compile time what type the `number`
|
|
|
|
|
variable is, definitively, so it can verify at compile time that its type is
|
|
|
|
|
valid everywhere we use `number`. Rust wouldn’t be able to do that if the type
|
|
|
|
|
of `number` was only determined at runtime; the compiler would be more complex
|
|
|
|
|
and would make fewer guarantees about the code if it had to keep track of
|
|
|
|
|
multiple hypothetical types for any variable.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-06 01:30:59 +08:00
|
|
|
|
### Repetition with Loops
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
It’s often useful to execute a block of code more than once. For this task,
|
|
|
|
|
Rust provides several *loops*. A loop runs through the code inside the loop
|
|
|
|
|
body to the end and then starts immediately back at the beginning. To
|
|
|
|
|
experiment with loops, let’s make a new project called *loops*.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Rust has three kinds of loops: `loop`, `while`, and `for`. Let’s try each one.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-06 01:30:59 +08:00
|
|
|
|
#### Repeating Code with `loop`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
The `loop` keyword tells Rust to execute a block of code over and over again
|
2016-11-02 04:47:25 +08:00
|
|
|
|
forever or until you explicitly tell it to stop.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
As an example, change the *src/main.rs* file in your *loops* directory to look
|
2016-08-03 10:07:25 +08:00
|
|
|
|
like this:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust,ignore
|
|
|
|
|
fn main() {
|
|
|
|
|
loop {
|
|
|
|
|
println!("again!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When we run this program, we’ll see `again!` printed over and over continuously
|
2016-08-03 10:07:25 +08:00
|
|
|
|
until we stop the program manually. Most terminals support a keyboard shortcut,
|
2016-11-02 04:47:25 +08:00
|
|
|
|
ctrl-C, to halt a program that is stuck in a continual loop. Give it a try:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling loops v0.1.0 (file:///projects/loops)
|
|
|
|
|
Running `target/debug/loops`
|
|
|
|
|
again!
|
|
|
|
|
again!
|
|
|
|
|
again!
|
|
|
|
|
again!
|
|
|
|
|
^Cagain!
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
The symbol `^C` represents where you pressed ctrl-C. You may or may not see the
|
|
|
|
|
word `again!` printed after the `^C`, depending on where the code was in the
|
|
|
|
|
loop when it received the halt signal.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
Fortunately, Rust provides another, more reliable way to break out of a loop.
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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” section of Chapter 2 to exit the
|
|
|
|
|
program when the user won the game by guessing the correct number.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-06 01:30:59 +08:00
|
|
|
|
#### Conditional Loops with `while`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut number = 3;
|
|
|
|
|
|
|
|
|
|
while number != 0 {
|
|
|
|
|
println!("{}!", number);
|
|
|
|
|
|
|
|
|
|
number = number - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("LIFTOFF!!!");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-06 01:30:59 +08:00
|
|
|
|
#### Looping Through a Collection with `for`
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
You could use the `while` construct to loop over the elements of a collection,
|
|
|
|
|
such as an array. For example:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = [10, 20, 30, 40, 50];
|
|
|
|
|
let mut index = 0;
|
|
|
|
|
|
|
|
|
|
while index < 5 {
|
|
|
|
|
println!("the value is: {}", a[index]);
|
|
|
|
|
|
|
|
|
|
index = index + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
<caption>
|
|
|
|
|
Listing 3-5: Looping through each element of a collection using a `while` loop
|
|
|
|
|
</caption>
|
|
|
|
|
|
|
|
|
|
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:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ cargo run
|
|
|
|
|
Compiling loops v0.1.0 (file:///projects/loops)
|
|
|
|
|
Running `target/debug/loops`
|
|
|
|
|
the value is: 10
|
|
|
|
|
the value is: 20
|
|
|
|
|
the value is: 30
|
|
|
|
|
the value is: 40
|
|
|
|
|
the value is: 50
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
All five array values appear in the terminal, as expected. Even though `index`
|
|
|
|
|
will reach a value of `6` at some point, the loop stops executing before trying
|
|
|
|
|
to fetch a sixth value from the array.
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
But this approach is error prone; we could cause the program to panic if the
|
|
|
|
|
index length is incorrect. It’s also slow, because the compiler needs to
|
2016-08-03 10:07:25 +08:00
|
|
|
|
perform the conditional check on every element on every iteration through the
|
|
|
|
|
loop.
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
As a more efficient alternative, you can use a `for` loop and execute some code
|
2016-08-03 10:07:25 +08:00
|
|
|
|
for each item in a collection. A `for` loop looks like this:
|
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = [10, 20, 30, 40, 50];
|
|
|
|
|
|
|
|
|
|
for element in a.iter() {
|
|
|
|
|
println!("the value is: {}", element);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
<caption>
|
|
|
|
|
Listing 3-6: Looping through each element of a collection using a `for` loop
|
|
|
|
|
</caption>
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
When we run this code, we’ll see the same output as in Listing 3-5. 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.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
For example, in the code in Listing 3-5, 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
|
|
|
|
|
code if you changed the number of values in the array.
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
|
|
|
|
The safety and conciseness of `for` loops make them the most commonly used loop
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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-5, 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.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
Here’s what the countdown would look like using a `for` loop and another method
|
|
|
|
|
we’ve not yet talked about, `rev`, to reverse the range:
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-08-20 05:13:41 +08:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2016-08-03 10:07:25 +08:00
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
for number in (1..4).rev() {
|
|
|
|
|
println!("{}!", number);
|
|
|
|
|
}
|
|
|
|
|
println!("LIFTOFF!!!");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
This code is a bit nicer, isn’t it?
|
2016-08-03 10:07:25 +08:00
|
|
|
|
|
2016-09-21 22:54:36 +08:00
|
|
|
|
## Summary
|
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
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:
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-02 04:47:25 +08:00
|
|
|
|
* Convert temperatures between Fahrenheit and Celsius.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
* Generate the nth Fibonacci number.
|
2016-11-02 04:47:25 +08:00
|
|
|
|
* Print the lyrics to the Christmas carol “The Twelve Days of Christmas,”
|
|
|
|
|
taking advantage of the repetition in the song.
|
2016-09-21 22:54:36 +08:00
|
|
|
|
|
2016-11-01 08:27:36 +08:00
|
|
|
|
When you’re ready to move on, we’ll talk about a concept in Rust that *doesn’t*
|
2016-09-21 22:54:36 +08:00
|
|
|
|
commonly exist in other programming languages: ownership.
|