First third to nostarch!

This commit is contained in:
Carol (Nichols || Goulding) 2016-08-02 22:07:25 -04:00
parent 92085ebd8f
commit bdc30d4090
9 changed files with 4181 additions and 4072 deletions

492
nostarch/chapter01.md Normal file
View File

@ -0,0 +1,492 @@
# Introduction
Welcome to “The Rust Programming Language”, an introductory book about Rust.
Rust is a programming language thats focused on safety, speed, and
concurrency. Its design lets you create programs that have the performance and
control of a low-level language, but with helpful abstractions that feel like a
high-level language. The Rust community welcomes all programmers who have their
experience in languages like C and are looking for a safer alternative, as well
as programmers from languages like Python who are looking for ways to write more
performant code without losing expressiveness.
Rust provides the majority of its safety checks at compile time and without a
garbage collector so that your program's runtime isn't impacted. This makes it
useful in a number of use cases that other languages arent good at: embedding
in other languages, programs with specific space and time requirements, and
writing low-level code, like device drivers and operating systems. It's also
great for web applications: it powers the Rust package registry site, crates.io!
We're excited to see what _you_ create with Rust.
This book is written for a reader who already knows how to program in at least
one programming language. After reading this book, you should be comfortable
writing Rust programs. Well be learning Rust through small, focused examples
that build on each other to demonstrate how to use various features of Rust as
well as how they work behind the scenes.
## Contributing to the book
This book is open source. If you find an error, please dont hesitate to file an
issue or send a pull request [on GitHub].
[on GitHub]: https://github.com/rust-lang/book
## Installation
The first step to using Rust is to install it. Youll need an internet
connection to run the commands in this chapter, as well be downloading Rust
from the internet.
Well be showing off a number of commands using a terminal, and those lines all
start with `$`. You don't need to type in the `$`s; they are there to indicate
the start of each command. Youll see many tutorials and examples around the web
that follow this convention: `$` for commands run as a regular user, and `#`
for commands you should be running as an administrator. Lines that don't start
with `$` are typically showing the output of the previous command.
### Installing on Linux or Mac
If you're on Linux or a Mac, all you need to do is open a terminal and type
this:
```bash
$ curl https://sh.rustup.rs -sSf | sh
```
This will download a script and start the installation. You may be prompted for
your password. If it all goes well, youll see this appear:
```bash
Rust is installed now. Great!
```
### Installing on Windows
If you're on Windows, please download the appropriate [installer][install-page].
[install-page]: https://www.rust-lang.org/install.html
### Uninstalling
Uninstalling Rust is as easy as installing it. On Linux or Mac, just run
the uninstall script:
```bash
$ rustup self uninstall
```
If you used the Windows installer, you can re-run the `.msi` and it will give
you an uninstall option.
### Troubleshooting
If you've got Rust installed, you can open up a shell, and type this:
```bash
$ rustc --version
```
You should see the version number, commit hash, and commit date in a format
similar to this for the latest stable version at the time you install:
```bash
rustc x.y.z (abcabcabc yyyy-mm-dd)
```
If you see this, Rust has been installed successfully!
Congrats!
If you don't and you're on Windows, check that Rust is in your `%PATH%` system
variable. If it isn't, run the installer again, select "Change" on the "Change,
repair, or remove installation" page and ensure "Add to PATH" is checked.
If not, there are a number of places where you can get help. The easiest is
[the #rust IRC channel on irc.mozilla.org][irc], which you can access through
[Mibbit][mibbit]. Click that link, and you'll be chatting with other Rustaceans
(a silly nickname we call ourselves) who can help you out. Other great resources
include [the users forum][users] and [Stack Overflow][stackoverflow].
[irc]: irc://irc.mozilla.org/#rust
[mibbit]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust
[users]: https://users.rust-lang.org/
[stackoverflow]: http://stackoverflow.com/questions/tagged/rust
### Local documentation
The installer also includes a copy of the documentation locally, so you can
read it offline. On Linux or Mac, run `rustup doc` to open the local
documentation in your browser. On Windows, the documentation is in a
`share/doc` directory inside the directory where Rust was installed.
## Hello, World!
Now that you have Rust installed, lets write your first Rust program. It's
traditional when learning a new language to write a little program to print the
text “Hello, world!” to the screen, and in this section, we'll follow that
tradition.
> Note: This book assumes basic familiarity with the command line. Rust itself
> makes no specific demands about your editing, tooling, or where your code
> lives, so if you prefer an IDE to the command line, that's an option.
### Creating a Project File
First, make a file to put your Rust code in. Rust doesn't care where your code
lives, but for this book, we'd suggest making a *projects* directory in your
home directory and keeping all your projects there. Open a terminal and enter
the following commands to make a directory for this particular project:
```bash
$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world
```
> Note: If youre on Windows and not using PowerShell, the `~` that represents
> your home directory may not work.
> Consult the documentation for your shell for more details.
### Writing and Running a Rust Program
Next, make a new source file and call it *main.rs*. Rust files always end with
the *.rs* extension. If youre using more than one word in your filename, use
an underscore to separate them. For example, you'd use *hello_world.rs* rather
than *helloworld.rs*.
Now open the *main.rs* file you just created, and type the following code:
```rust
fn main() {
println!("Hello, world!");
}
```
Save the file, and go back to your terminal window. On Linux or OSX, enter the
following commands:
```bash
$ rustc main.rs
$ ./main
Hello, world!
```
On Windows, just replace `main` with `main.exe`. Regardless of your operating
system, you should see the string `Hello, world!` print to the terminal. If you
did, then congratulations! You've officially written a Rust program. That makes
you a Rust programmer! Welcome.
### Anatomy of a Rust Program
Now, lets go over what just happened in your "Hello, world!" program in
detail. Here's the first piece of the puzzle:
```rust
fn main() {
}
```
These lines define a *function* in Rust. The `main` function is special: it's
the first thing that is run for every executable Rust program. The first line
says, “Im declaring a function named `main` that takes no arguments and
returns nothing.” If there were arguments, they would go inside the parentheses
(`(` and `)`). We arent returning anything from this function, so we have
omitted the return type entirely. If there was a return type, there would be a
`->` and the return type after the parentheses.
Also note that the function body is wrapped in curly braces (`{` and `}`). Rust
requires these around all function bodies. It's considered good style to put
the opening curly brace on the same line as the function declaration, with one
space in between.
Inside the `main()` function:
```rust
println!("Hello, world!");
```
This line does all of the work in this little program: it prints text to the
screen. There are a number of details that are important here. The first is
that its indented with four spaces, not tabs.
The second important part is `println!()`. This is calling a Rust *macro*,
which is how metaprogramming is done in Rust. If it were calling a function
instead, it would look like this: `println()` (without the `!`). We'll discuss
Rust macros in more detail in Chapter XX, but for now you just need to know
that when you see a `!` that means that youre calling a macro instead of a
normal function.
Next is `"Hello, world!"` which is a *string*. We pass this string as an
argument to `println!()`, which prints the string to the screen. Easy enough!
The line ends with a semicolon (`;`). The `;` indicates that this expression is
over, and the next one is ready to begin. Most lines of Rust code end with a
`;`.
### Compiling and Running Are Separate Steps
In "Writing and Running a Rust Program", we showed you how to run a newly
created program. We'll break that process down and examine each step now.
Before running a Rust program, you have to compile it. You can use the Rust
compiler by entering the `rustc` command and passing it the name of your source
file, like this:
```bash
$ rustc main.rs
```
If you come from a C or C++ background, you'll notice that this is similar to
`gcc` or `clang`. After compiling successfully, Rust should output a binary
executable, which you can see on Linux or OSX by entering the `ls` command in
your shell as follows:
```bash
$ ls
main main.rs
```
On Windows, you'd enter:
```bash
$ dir
main.exe main.rs
```
This shows we have two files: the source code, with the `.rs` extension, and the
executable (`main.exe` on Windows, `main` everywhere else). All that's left to
do from here is run the `main` or `main.exe` file, like this:
```bash
$ ./main # or main.exe on Windows
```
If *main.rs* were your "Hello, world!" program, this would print `Hello,
world!` to your terminal.
If you come from a dynamic language like Ruby, Python, or JavaScript, you may
not be used to compiling and running a program being separate steps. Rust is an
*ahead-of-time compiled* language, which means that you can compile a program,
give it to someone else, and they can run it even without having Rust
installed. If you give someone a `.rb`, `.py`, or `.js` file, on the other
hand, they need to have a Ruby, Python, or JavaScript implementation installed
(respectively), but you only need one command to both compile and run your
program. Everything is a tradeoff in language design.
Just compiling with `rustc` is fine for simple programs, but as your project
grows, you'll want to be able to manage all of the options your project has
and make it easy to share your code with other people and projects. Next, we'll
introduce you to a tool called Cargo, which will help you write real-world Rust
programs.
## Hello, Cargo!
Cargo is Rusts build system and package manager, and Rustaceans use Cargo to
manage their Rust projects because it makes a lot of tasks easier. For example,
Cargo takes care of building your code, downloading the libraries your code
depends on, and building those libraries. We call libraries your code needs
dependencies since your code depends on them.
The simplest Rust programs, like the one we've written so far, dont have any
dependencies, so right now, you'd only be using the part of Cargo that can take
care of building your code. As you write more complex Rust programs, youll
want to add dependencies, and if you start off using Cargo, that will be a lot
easier to do.
As the vast, vast majority of Rust projects use Cargo, we will assume that
youre using it for the rest of the book. Cargo comes installed with Rust
itself, if you used the official installers as covered in the Installation
chapter. If you installed Rust through some other means, you can check if you
have Cargo installed by typing the following into your terminal:
```bash
$ cargo --version
```
If you see a version number, great! If you see an error like `command not
found`, then you should look at the documentation for the way you installed
Rust to determine how to install Cargo separately.
### Creating a Project with Cargo
Let's create a new project using Cargo and look at how it differs from our
project in `hello_world`. Go back to your projects directory (or wherever you
decided to put your code):
```bash
$ cd ~/projects
```
And then run:
```bash
$ cargo new hello_cargo --bin
$ cd hello_cargo
```
We passed the `--bin` argument to `cargo new` because our goal is to make an
executable application, as opposed to a library. Executables are often called
*binaries* (as in `/usr/bin`, if youre on a Unix system). `hello_cargo` is the
name we've chosen for our project, and Cargo creates its files in a directory
of the same name that we can then go into.
If we list the files in the `hello_cargo` directory, we can see that Cargo has
generated two files and one directory for us: a `Cargo.toml` and a *src*
directory with a *main.rs* file inside. It has also initialized a new `git`
repository in the `hello_cargo` directory for us; you can change this to use a
different version control system, or no version control system, by using the
`--vcs` flag.
Open up `Cargo.toml` in your text editor of choice. It should look something
like this:
```toml
[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
[dependencies]
```
This file is in the *[TOML]* (Tom's Obvious, Minimal Language) format. TOML is
similar to INI but has some extra goodies and is used as Cargos
configuration format.
[TOML]: https://github.com/toml-lang/toml
The first line, `[package]`, is a section heading that indicates that the
following statements are configuring a package. As we add more information to
this file, well add other sections.
The next three lines set the three bits of configuration that Cargo needs to
see in order to know that it should compile your program: its name, what
version it is, and who wrote it. Cargo gets your name and email information
from your environment. If its not correct, go ahead and fix that and save the
file.
The last line, `[dependencies]`, is the start of a section for you to list any
crates that your project will depend on so that Cargo knows to download and
compile those too. We won't need any other crates for this project, but we will
in the guessing game tutorial in the next chapter.
Now let's look at `src/main.rs`:
```rust
fn main() {
println!("Hello, world!");
}
```
Cargo has generated a "Hello World!" for you, just like the one we wrote
earlier! So that part is the same. The differences between our previous project
and the project generated by Cargo that we've seen so far are:
1. Our code goes in the `src` directory
2. The top level contains a `Cargo.toml` configuration file
Cargo expects your source files to live inside the *src* directory so that the
top-level project directory is just for READMEs, license information,
configuration files, and anything else not related to your code. In this way,
using Cargo helps you keep your projects nice and tidy. There's a place for
everything, and everything is in its place.
If you started a project that doesn't use Cargo, as we did with our project in
the `hello_world` directory, you can convert it to a project that does use
Cargo by moving your code into the `src` directory and creating an appropriate
`Cargo.toml`.
### Building and Running a Cargo Project
Now let's look at what's different about building and running your Hello World
program through Cargo! To do so, enter the following commands:
```bash
$ cargo build
Compiling hello_cargo v0.1.0 (file:///home/yourname/projects/hello_cargo)
```
This should have created an executable file in `target/debug/hello_cargo` (or `target/debug/hello_cargo.exe` on Windows), which you can run with this command:
```bash
$ ./target/debug/hello_cargo # or ./target/debug/hello_cargo.exe on Windows
Hello, world!
```
Bam! If all goes well, `Hello, world!` should print to the terminal once more.
Running `cargo build` for the first time also causes Cargo to create a new file
at the top level called *Cargo.lock*, which looks like this:
```toml
[root]
name = "hello_cargo"
version = "0.1.0"
```
Cargo uses the *Cargo.lock* file to keep track of dependencies in your
application. This project doesn't have dependencies, so the file is a bit
sparse. Realistically, you won't ever need to touch this file yourself; just
let Cargo handle it.
We just built a project with `cargo build` and ran it with
`./target/debug/hello_cargo`, but we can actually do both in one step with
`cargo run` as follows:
```bash
$ cargo run
Running `target/debug/hello_cargo`
Hello, world!
```
Notice that this time, we didn't see the output that Cargo was compiling
`hello_cargo`. Cargo figured out that the files havent changed, so it just ran
the binary. If you had modified your source code, Cargo would have rebuilt the
project before running it, and you would have seen something like this:
```bash
$ cargo run
Compiling hello_cargo v0.1.0 (file:///home/yourname/projects/hello_cargo)
Running `target/debug/hello_cargo`
Hello, world!
```
So a few more differences we've now seen:
3. Instead of using `rustc`, build a project using `cargo build` (or build and run it in one step with `cargo run`)
4. Instead of the result of the build being put in the same directory as our code, Cargo will put it in the `target/debug` directory.
### Building for Release
When your project is finally ready for release, you can use `cargo build
--release` to compile your project with optimizations. This will create an
executable in `target/release` instead of `target/debug`. These optimizations
make your Rust code run faster, but turning them on makes your program take
longer to compile. This is why there are two different profiles: one for
development when you want to be able to rebuild quickly and often, and one for
building the final program youll give to a user that won't be rebuilt and
that we want to run as fast as possible. If you're benchmarking the running
time of your code, be sure to run `cargo build --release` and benchmark with
the executable in `target/release`.
### Cargo as Convention
With simple projects, Cargo doesn't provide a whole lot of value over just
using `rustc`, but it will prove its worth as you continue. With complex
projects composed of multiple crates, its much easier to let Cargo coordinate
the build. With Cargo, you can just run `cargo build`, and it should work the
right way. Even though this project is simple, it now uses much of the real
tooling youll use for the rest of your Rust career. In fact, you can get
started with virtually all Rust projects you might find that you want to work
on with the following commands:
```bash
$ git clone someurl.com/someproject
$ cd someproject
$ cargo build
```
> Note: If you want to look at Cargo in more detail, check out the official
[Cargo guide], which covers all of its features.
[Cargo guide]: http://doc.crates.io/guide.html

966
nostarch/chapter02.md Normal file
View File

@ -0,0 +1,966 @@
# Guessing Game
Let's jump into Rust with a hands-on project! Well implement a classic
beginner programming problem: the guessing game. Heres how it works: Our
program will generate a random integer between one and a hundred. It will then
prompt us to enter a guess. Upon entering our guess, it will tell us if were
too low or too high. Once we guess correctly, it will congratulate us.
## Set up
Lets set up a new project. Go to your projects directory, and create a new project using Cargo.
```bash
$ cd ~/projects
$ cargo new guessing_game --bin
$ cd guessing_game
```
We pass the name of our project to `cargo new`, then the `--bin` flag, since
were going to be making another binary like in Chapter 1.
Take a look at the generated `Cargo.toml`:
```toml
[package]
name = "guessing_game"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
[dependencies]
```
If the authors information that Cargo got from your environment is not correct,
go ahead and fix that.
And as we saw in the last chapter, `cargo new` generates a Hello, world! for
us. Check out `src/main.rs`:
```rust
fn main() {
println!("Hello, world!");
}
```
Lets try compiling what Cargo gave us and running it in the same step, using the `cargo run` command:
```bash
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Hello, world!
```
Great! The `run` command comes in handy when you need to rapidly iterate on a
project. Our game is such a project: we want to quickly test each
iteration before moving on to the next one.
Now open up your `src/main.rs` again. Well be writing all of our code in this
file.
## Processing a Guess
Lets get to it! The first thing we need to do for our guessing game is
allow our player to input a guess. Put this in your `src/main.rs`:
```rust,ignore
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
```
Theres a lot here! Lets go over it, bit by bit.
```rust,ignore
use std::io;
```
Well need to take user input and then print the result as output. As such, we
need the `io` library from the standard library. Rust only imports a few things
by default into every program, [the prelude][prelude]. If its not in the
prelude, youll have to `use` it directly. Using the `std::io` library gets
you a number of useful `io`-related things, so that's what we've done here.
[prelude]: ../std/prelude/index.html
```rust,ignore
fn main() {
```
As youve seen in Chapter 1, the `main()` function is the entry point into the
program. The `fn` syntax declares a new function, the `()`s indicate that
there are no arguments, and `{` starts the body of the function.
```rust,ignore
println!("Guess the number!");
println!("Please input your guess.");
```
We previously learned in Chapter 1 that `println!()` is a macro that
prints a string to the screen.
### Variable Bindings
```rust,ignore
let mut guess = String::new();
```
Now were getting interesting! Theres a lot going on in this little line.
The first thing to notice is that this is a let statement, which is
used to create what are called variable bindings. Here's an example:
```rust,ignore
let foo = bar;
```
This will create a new binding named `foo`, and bind it to the value `bar`. In
many languages, this is called a variable, but Rusts variable bindings have
a few tricks up their sleeves.
For example, theyre immutable by default. Thats why our example
uses `mut`: it makes a binding mutable, rather than immutable.
```rust
let foo = 5; // immutable.
let mut bar = 5; // mutable
```
Oh, and `//` will start a comment, until the end of the line. Rust ignores
everything in comments.
So now we know that `let mut guess` will introduce a mutable binding named
`guess`, but we have to look at the other side of the `=` for what its
bound to: `String::new()`.
`String` is a string type, provided by the standard library. A
[`String`][string] is a growable, UTF-8 encoded bit of text.
[string]: ../std/string/struct.String.html
The `::new()` syntax uses `::` because this is an associated function of
a particular type. That is to say, its associated with `String` itself,
rather than a particular instance of a `String`. Some languages call this a
static method.
This function is named `new()`, because it creates a new, empty `String`.
Youll find a `new()` function on many types, as its a common name for making
a new value of some kind.
Lets move forward:
```rust,ignore
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
```
Lets go through this together bit-by-bit. The first line has two parts. Heres
the first:
```rust,ignore
io::stdin()
```
Remember how we `use`d `std::io` on the first line of the program? Were now
calling an associated function on it. If we didnt `use std::io`, we could
have written this line as `std::io::stdin()`.
This particular function returns a handle to the standard input for your
terminal. More specifically, a [std::io::Stdin][iostdin].
[iostdin]: ../std/io/struct.Stdin.html
The next part will use this handle to get input from the user:
```rust,ignore
.read_line(&mut guess)
```
Here, we call the [`read_line()`][read_line] method on our handle. Were also
passing one argument to `read_line()`: `&mut guess`.
[read_line]: ../std/io/struct.Stdin.html#method.read_line
Remember how we bound `guess` above? We said it was mutable. However,
`read_line` doesnt take a `String` as an argument: it takes a `&mut String`.
The `&` is the feature of Rust called a reference, which allows you to have
multiple ways to access one piece of data in order to reduce copying.
References are a complex feature, as one of Rusts major selling points is how
safe and easy it is to use references. We dont need to know a lot of those
details to finish our program right now, though; Chapter XX will cover them in
more detail. For now, all we need to know is that like `let` bindings,
references are immutable by default. Hence, we need to write `&mut guess`,
rather than `&guess`.
Why does `read_line()` take a mutable reference to a string? Its job is
to take what the user types into standard input and place that into a
string. So it takes that string as an argument, and in order to add
the input, that string needs to be mutable.
But were not quite done with this line of code, though. While its
a single line of text, its only the first part of the single logical line of
code. This is the second part of the line:
```rust,ignore
.expect("Failed to read line");
```
When you call a method with the `.foo()` syntax, you may introduce a newline
and other whitespace. This helps you split up long lines. We _could_ have
written this code as:
```rust,ignore
io::stdin().read_line(&mut guess).expect("failed to read line");
```
But that gets hard to read. So weve split it up, two lines for two method
calls.
### The `Result` Type
We already talked about `read_line()`, but what about `expect()`? Well,
we already mentioned that `read_line()` puts what the user types into the `&mut
String` we pass it. But it also returns a value: in this case, an
[`io::Result`][ioresult]. Rust has a number of types named `Result` in its
standard library: a generic [`Result`][result], and then specific versions for
sub-libraries, like `io::Result`.
[ioresult]: ../std/io/type.Result.html
[result]: ../std/result/enum.Result.html
The purpose of these `Result` types is to encode error handling information.
Values of the `Result` type, like any type, have methods defined on them. In
this case, `io::Result` has an [`expect()` method][expect] that takes a value
its called on, and if it isnt a successful result, will cause our program to
crash and display the message that we passed as an argument to `expect()`.
[expect]: ../std/result/enum.Result.html#method.expect
If we don't call this method, our program will compile, but well get a warning:
```bash
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
src/main.rs:10:5: 10:39 warning: unused result which must be used,
#[warn(unused_must_use)] on by default
src/main.rs:10 io::stdin().read_line(&mut guess);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
Rust warns us that we havent used the `Result` value. This warning comes from
a special annotation that `io::Result` has. Rust is trying to tell you that you
havent handled a possible error. The right way to suppress the error is to
actually write error handling. Luckily, if we want to crash if theres a
problem, we can use `expect()`. If we can recover from the error somehow, wed
do something else, but well save that for a future project.
### `println!()` Placeholders
Theres only one line of this first example left, aside from the closing curly
brace:
```rust,ignore
println!("You guessed: {}", guess);
}
```
This prints out the string we saved our input in. The `{}`s are a placeholder:
think of `{}` as little crab pincers, holding a value in place. The first `{}`
holds the first value after the format string, the second set holds the second
value, and so on. Printing out multiple values in one call to `println!()` would then look like this:
```rust
let x = 5;
let y = 10;
println!("x and y: {} and {}", x, y);
```
Which would print out "x and y: 5 and 10".
Anyway, back to our guessing game. We can run what we have with `cargo run`:
```bash
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
6
You guessed: 6
```
All right! Our first part is done: we can get input from the keyboard and then
print it back out.
## Generating a secret number
Next, we need to generate a secret number. Rust does not yet include random
number functionality in its standard library. The Rust team does, however,
provide a [`rand` crate][randcrate]. A crate is a package of Rust code.
Weve been building a binary crate, which is an executable. `rand` is a
library crate, which contains code thats intended to be used with other
programs.
[randcrate]: https://crates.io/crates/rand
Using external crates is where Cargo really shines. Before we can write
the code using `rand`, we need to modify our `Cargo.toml`. Open it up, and
add this line at the bottom beneath the `[dependencies]` section header that
Cargo created for you:
```toml
[dependencies]
rand = "0.3.14"
```
The `[dependencies]` section of `Cargo.toml` is like the `[package]` section:
everything that follows the section heading is part of that section, until
another section starts. Cargo uses the dependencies section to know what
dependencies on external crates you have and what versions of those crates you
require. In this case, weve specified the `rand` crate with the semantic
version specifier `0.3.14`. Cargo understands [Semantic Versioning][semver], a
standard for writing version numbers. A bare number like above is actually
shorthand for `^0.3.14`, which means "any version that has a public API
compatible with version 0.3.14".
[semver]: http://semver.org
Now, without changing any of our code, lets build our project:
```bash
$ cargo build
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.3.14
Downloading libc v0.2.14
Compiling libc v0.2.14
Compiling rand v0.3.14
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
```
You may see different versions (but they will be compatible, thanks to semver!)
and the lines may be in a different order.
Lots of new output! Now that we have an external dependency, Cargo fetches the
latest versions of everything from the *registry*, which is a copy of data from
[Crates.io][cratesio]. Crates.io is where people in the Rust ecosystem
post their open source Rust projects for others to use.
[cratesio]: https://crates.io
After updating the registry, Cargo checks our `[dependencies]` and downloads
any we dont have yet. In this case, while we only said we wanted to depend on
`rand`, weve also grabbed a copy of `libc`. This is because `rand` depends on
`libc` to work. After downloading them, it compiles them and then compiles
our project.
If we run `cargo build` again, well get different output:
```bash
$ cargo build
```
Thats right, no output! Cargo knows that our project has been built, that
all of its dependencies are built, and that no changes have been made. Theres
no reason to do all that stuff again. With nothing to do, it simply
exits. If we open up `src/main.rs`, make a trivial change, then save it again,
well only see one line:
```bash
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
```
What happens when next week version `v0.3.15` of the `rand` crate comes out,
with an important bugfix? While getting bugfixes is important, what if `0.3.15`
contains a regression that breaks our code?
The answer to this problem is the `Cargo.lock` file created the first time we
ran `cargo build` that is now in your project directory. When you build your
project for the first time, Cargo figures out all of the versions that fit your
criteria then writes them to the `Cargo.lock` file. When you build your project
in the future, Cargo will see that the `Cargo.lock` file exists and then use
that specific version rather than do all the work of figuring out versions
again. This lets you have a repeatable build automatically. In other words,
well stay at `0.3.14` until we explicitly upgrade, and so will anyone who we
share our code with, thanks to the lock file.
What about when we _do_ want to use `v0.3.15`? Cargo has another command,
`update`, which says ignore the `Cargo.lock` file and figure out all the
latest versions that fit what weve specified in `Cargo.toml`. If that works,
write those versions out to the lock file. But by default, Cargo will only
look for versions larger than `0.3.0` and smaller than `0.4.0`. If we want to
move to `0.4.x`, wed have to update what is in the `Cargo.toml` file. When we
do, the next time we `cargo build`, Cargo will update the index and re-evaluate
our `rand` requirements.
Theres a lot more to say about [Cargo][doccargo] and [its
ecosystem][doccratesio] that we will get into in Chapter XX, but for now,
thats all we need to know. Cargo makes it really easy to re-use libraries, so
Rustaceans are able to write smaller projects which are assembled out of a
number of sub-packages.
[doccargo]: http://doc.crates.io
[doccratesio]: http://doc.crates.io/crates-io.html
Lets get on to actually _using_ `rand`. Heres our next step:
```rust,ignore
extern crate rand;
use std::io;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
println!("You guessed: {}", guess);
}
```
The first thing weve done is change the first line. It now says `extern crate
rand`. Because we declared `rand` in our `[dependencies]`, we can now put
`extern crate` in our code to let Rust know well be making use of that
dependency. This also does the equivalent of a `use rand;` as well, so we can
call anything in the `rand` crate by prefixing it with `rand::`.
Next, we added another `use` line: `use rand::Rng`. Were going to use a
method in a moment, and it requires that `Rng` be in scope to work. The basic
idea is this: methods are defined on something called traits, and for the
method to work, it needs the trait to be in scope. For more about the
details, read the traits section in Chapter XX.
There are two other lines we added, in the middle:
```rust,ignore
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
```
We use the `rand::thread_rng()` function to get a copy of the random number
generator, which is local to the particular thread of execution
were in. Because we put `use rand::Rng` above, the random number generator has
a `gen_range()` method available. This method takes two numbers as arguments
and generates a random number between them. Its inclusive on the lower bound
but exclusive on the upper bound, so we need `1` and `101` to ask for a number
ranging from one to a hundred.
The second line prints out the secret number. This is useful while
were developing our program to let us easily test it out, but well be
deleting it for the final version. Its not much of a game if it prints out
the answer when you start it up!
Try running our new program a few times:
```bash
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 7
Please input your guess.
4
You guessed: 4
$ cargo run
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 83
Please input your guess.
5
You guessed: 5
```
You should get different random numbers, and they should all be between 1 and
100. Great job! Next up: comparing our guess to the secret number.
## Comparing guesses
Now that weve got user input, lets compare our guess to the secret number.
Heres part of our next step. It won't quite compile yet though:
```rust,ignore
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
```
A few new bits here. The first is another `use`. We bring a type called
`std::cmp::Ordering` into scope. Then we add five new lines at the bottom that
use that type:
```rust,ignore
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
```
The `cmp()` method can be called on anything that can be compared, and it
takes a reference to the thing you want to compare it to. It returns the
`Ordering` type we `use`d earlier. We use a [`match`][match] statement to
determine exactly what kind of `Ordering` it is. `Ordering` is an
[`enum`][enum], short for enumeration, which looks like this:
```rust
enum Foo {
Bar,
Baz,
}
```
[match]: match.html
[enum]: enums.html
With this definition, anything of type `Foo` can be either a
`Foo::Bar` or a `Foo::Baz`. We use the `::` to indicate the
namespace for a particular `enum` variant.
The [`Ordering`][ordering] `enum` has three possible variants: `Less`, `Equal`,
and `Greater`. The `match` statement takes a value of a type and lets you
create an arm for each possible value. An arm is made up of a pattern and the
code that we should execute if the pattern matches the value of the type. Since
we have three types of `Ordering`, we have three arms:
```rust,ignore
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
```
[ordering]: ../std/cmp/enum.Ordering.html
If its `Less`, we print `Too small!`, if its `Greater`, `Too big!`, and if
`Equal`, `You win!`. `match` is really useful and is used often in Rust.
We did mention that this wont quite compile yet, though. Lets try it:
```bash
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
src/main.rs:23:21: 23:35 error: mismatched types [E0308]
src/main.rs:23 match guess.cmp(&secret_number) {
^~~~~~~~~~~~~~
src/main.rs:23:21: 23:35 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:23:21: 23:35 note: expected type `&std::string::String`
src/main.rs:23:21: 23:35 note: found type `&_`
error: aborting due to previous error
Could not compile `guessing_game`.
```
Whew! This is a big error. The core of it is that we have mismatched types.
Rust has a strong, static type system. However, it also has type inference.
When we wrote `let guess = String::new()`, Rust was able to infer that `guess`
should be a `String`, so it doesnt make us write out the type. With our
`secret_number`, there are a number of types which can have a value between one
and a hundred: `i32`, a thirty-two-bit number, or `u32`, an unsigned
thirty-two-bit number, or `i64`, a sixty-four-bit number or others. So far,
that hasnt mattered, and so Rust defaults to an `i32`. However, here, Rust
doesnt know how to compare the `guess` and the `secret_number`. They need to
be the same type.
Ultimately, we want to convert the `String` we read as input
into a real number type so that we can compare it to the guess numerically. We
can do that with two more lines. Heres our new program:
```rust,ignore
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
```
The new two lines:
```rust,ignore
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
```
Wait a minute, didn't we already have a `guess`? We do, but Rust allows us
to shadow the previous `guess` with a new one. This is often used in this
exact situation, where `guess` starts as a `String`, but we want to convert it
to a `u32`. Shadowing lets us re-use the `guess` name rather than forcing us
to come up with two unique names like `guess_str` and `guess` or something
else.
We bind `guess` to an expression that looks like something we wrote earlier:
```rust,ignore
guess.trim().parse()
```
Here, `guess` refers to the old `guess`, the one that was a `String` with our
input in it. The `trim()` method on `String`s will eliminate any white space at
the beginning and end of our string. This is important, as we had to press the
return key to satisfy `read_line()`. If we type `5` and hit return, `guess`
looks like this: `5\n`. The `\n` represents newline, the enter key. `trim()`
gets rid of this, leaving our string with only the `5`.
The [`parse()` method on strings][parse] parses a string into some kind of
number. Since it can parse a variety of numbers, we need to give Rust a hint as
to the exact type of number we want. Hence, `let guess: u32`. The colon (`:`)
after `guess` tells Rust were going to annotate its type. `u32` is an
unsigned, thirty-two bit integer. Rust has a number of built-in number
types, but weve chosen `u32`. Its a good default choice for a small
positive number. You'll see the other number types in Chapter XX.
[parse]: ../std/primitive.str.html#method.parse
Just like `read_line()`, our call to `parse()` could cause an error. What if
our string contained `A👍%`? Thered be no way to convert that to a number. As
such, well do the same thing we did with `read_line()`: use the `expect()`
method to crash if theres an error.
Lets try our program out!
```bash
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/guessing_game`
Guess the number!
The secret number is: 58
Please input your guess.
76
You guessed: 76
Too big!
```
Nice! You can see we even added spaces before our guess, and it still figured
out that we guessed 76. Run the program a few times. Verify that guessing
the secret number works, as well as guessing a number too small.
Now weve got most of the game working, but we can only make one guess. Lets
change that by adding loops!
## Looping
The `loop` keyword gives us an infinite loop. Lets add that in:
```rust,ignore
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
}
```
And try it out. But wait, didnt we just add an infinite loop? Yup. Remember
our discussion about `parse()`? If we give a non-number answer, the program
will crash and, therefore, quit. Observe:
```bash
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/guessing_game`
Guess the number!
The secret number is: 59
Please input your guess.
45
You guessed: 45
Too small!
Please input your guess.
60
You guessed: 60
Too big!
Please input your guess.
59
You guessed: 59
You win!
Please input your guess.
quit
thread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:785
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/guess` (exit code: 101)
```
Ha! `quit` actually quits. As does any other non-number input. Well, this is
suboptimal to say the least. First, lets actually quit when you win the game:
```rust,ignore
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
```
By adding the `break` line after the `You win!`, well exit the loop when we
win. Exiting the loop also means exiting the program, since the loop is the last
thing in `main()`. We have another tweak to make: when someone inputs a
non-number, we dont want to quit, we want to ignore it. We can do that
like this:
```rust,ignore
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
```
These are the lines that changed:
```rust,ignore
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
```
This is how you generally move from crash on error to actually handle the
error: by switching from `expect()` to a `match` statement. A `Result` is the
return type of `parse()`. `Result` is an `enum` like `Ordering`, but in this
case, each variant has some data associated with it. `Ok` is a success, and
`Err` is a failure. Each contains more information: in this case, the
successfully parsed integer or an error type, respectively. When we `match` an
`Ok(num)`, that pattern sets the name `num` to the value inside the `Ok` (the
integer), and the code we run just returns that integer. In the `Err` case, we
dont care what kind of error it is, so we just use the catch-all `_` instead
of a name. So for all errors, we run the code `continue`, which lets us move to
the next iteration of the loop, effectively ignoring the errors.
Now we should be good! Lets try it:
```bash
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/guessing_game`
Guess the number!
The secret number is: 61
Please input your guess.
10
You guessed: 10
Too small!
Please input your guess.
99
You guessed: 99
Too big!
Please input your guess.
foo
Please input your guess.
61
You guessed: 61
You win!
```
Awesome! With one tiny last tweak, we can finish the guessing game. Can you
think of what it is? Thats right, we dont want to print out the secret
number. It was good for testing, but it kind of ruins the game. Heres our
final source:
```rust,ignore
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
```
## Complete!
This project showed you a lot: `let`, `match`, methods, associated
functions, using external crates, and more.
At this point, you have successfully built the Guessing Game! Congratulations!

1303
nostarch/chapter03.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

486
nostarch/chapter05.md Normal file
View File

@ -0,0 +1,486 @@
# Structs
A `struct`, short for "structure", gives us the ability to name and package
together multiple related values that make up a meaningful group. If you come
from an object-oriented language, a `struct` is like an object's data
attributes. `struct` and `enum` (that we talked about in the last chapter) are
the building blocks you can use in Rust to create new types in your program's
domain in order to take full advantage of Rust's compile-time type checking.
Lets write a program which calculates the distance between two points.
Well start off with single variable bindings, and then refactor it to
use `struct`s instead.
Lets make a new project with Cargo:
```bash
$ cargo new --bin points
$ cd points
```
Heres a short program which calculates the distance between two points. Put
it into your `src/main.rs`:
```rust
fn main() {
let x1 = 0.0;
let y1 = 5.0;
let x2 = 12.0;
let y2 = 0.0;
let answer = distance(x1, y1, x2, y2);
println!("Point 1: ({}, {})", x1, y1);
println!("Point 2: ({}, {})", x2, y2);
println!("Distance: {}", answer);
}
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
let x_squared = f64::powi(x2 - x1, 2);
let y_squared = f64::powi(y2 - y1, 2);
f64::sqrt(x_squared + y_squared)
}
```
Let's try running this program with `cargo run`:
```bash
$ cargo run
Compiling points v0.1.0 (file:///projects/points)
Running `target/debug/points`
Point 1: (0, 5)
Point 2: (12, 0)
Distance: 13
```
Let's take a quick look at `distance()` before we move forward. To find the
distance between two points, we can use the Pythagorean Theorem. The theorem is
named after Pythagoras, who was the first person to mathematically prove this
formula. The details aren't that important; just know the theorem says that the
formula for the distance between two points is equal to:
- squaring the distance between the points horizontally (the "x" direction)
- squaring the distance between the points vertically (the "y" direction)
- adding those together
- and taking the square root of that.
So that's what we're implementing here.
```rust,ignore
f64::powi(2.0, 3)
```
The double colon (`::`) here is a namespace operator. We havent talked about
modules and namespaces in depth yet, but you can think of the `powi()` function
as being scoped inside of another name. In this case, the name is `f64`, the
same as the type. The `powi()` function takes two arguments: the first is a
number, and the second is the power that it raises that number to. In this
case, the second number is an integer, hence the i in its name. Similarly,
`sqrt()` is a function under the `f64` module, which takes the square root of
its argument.
## Why `struct`s?
Our little program is okay, but we can do better. The key to seeing this is in
the signature of `distance()`:
```rust,ignore
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
```
The distance function is supposed to calculate the distance between two points.
But our distance function calculates some distance between four numbers. The
first two and last two arguments are related, but thats not expressed anywhere
in our program itself. It would be nicer if we had a way to group `(x1, y1)`
and `(x2, y2)` together.
Weve already discussed one way to do that: tuples. Heres a version of our
program which uses tuples:
```rust
fn main() {
let p1 = (0.0, 5.0);
let p2 = (12.0, 0.0);
let answer = distance(p1, p2);
println!("Point 1: {:?}", p1);
println!("Point 2: {:?}", p2);
println!("Distance: {}", answer);
}
fn distance(p1: (f64, f64), p2: (f64, f64)) -> f64 {
let x_squared = f64::powi(p2.0 - p1.0, 2);
let y_squared = f64::powi(p2.1 - p1.1, 2);
f64::sqrt(x_squared + y_squared)
}
```
This is a little better, for sure. Tuples let us add a little bit of structure.
Were now passing two arguments, so thats more clear. But its also worse:
tuples dont give names to their elements, so our calculation has gotten more
confusing:
```rust,ignore
p2.0 - p1.0
p2.1 - p1.1
```
When writing this example, your authors almost got it wrong themselves! Distance
is all about `x` and `y` points, but our code is talking about `0` and `1`.
This isnt great.
Enter `struct`s. We can transform our tuples into something with a name for the
whole as well as names for the parts:
```rust,ignore
let p1 = (0.0, 5.0);
struct Point {
x: f64,
y: f64,
}
let p1 = Point { x: 0.0, y: 5.0 };
```
Here we've defined a `struct` and given it the name `Point`. The parts inside
`{}` are defining the _fields_ of the struct. We can have as many or as few of
them as we'd like, and we give them a name and specify their type. Here we have
two fields named `x` and `y`, and they both hold `f64`s.
We can access the field of a struct in the same way we access an element of
a tuple, except we use its name:
```rust,ignore
let p1 = (0.0, 5.0);
let x = p1.0;
struct Point {
x: f64,
y: f64,
}
let p1 = Point { x: 0.0, y: 5.0 };
let x = p1.x;
```
Lets convert our program to use our `Point` `struct`. Heres what it looks
like now:
```rust
#[derive(Debug,Copy,Clone)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p1 = Point { x: 0.0, y: 5.0};
let p2 = Point { x: 12.0, y: 0.0};
let answer = distance(p1, p2);
println!("Point 1: {:?}", p1);
println!("Point 2: {:?}", p2);
println!("Distance: {}", answer);
}
fn distance(p1: Point, p2: Point) -> f64 {
let x_squared = f64::powi(p2.x - p1.x, 2);
let y_squared = f64::powi(p2.y - p1.y, 2);
f64::sqrt(x_squared + y_squared)
}
```
Our function signature for `distance()` now says exactly what we mean: it
calculates the distance between two `Point`s. And rather than `0` and `1`,
weve got back our `x` and `y`. This is a win for clarity.
## Derived Traits
Theres one other thing thats a bit strange here, this stuff above the
`struct` declaration:
```rust,ignore
#[derive(Debug,Copy,Clone)]
struct Point {
```
This is an annotation that tells the compiler our struct should get some
default behavior for the `Debug`, `Copy`, and `Clone` traits. We talked about
marking that types can be `Copy` and `Clone`-able in Chapter XX when we
discussed ownership. `Debug` is the trait that enables us to print out our
struct so that we can see its value while we are debugging our code.
So far, weve been printing values using `{}` in a `println!` macro. If we try
that with a struct, however, by default, we'll get an error. Say we have the
following program:
```rust,ignore
struct Point {
x: f64,
y: f64,
}
fn main() {
let p1 = Point { x: 0.0, y: 5.0};
println!("Point 1: {}", p1);
}
```
This code tries to print the `p1` point directly, which may seem innocuous. But
running it produces the following output:
```bash
$ cargo run
Compiling points v0.1.0 (file:///projects/points)
error: the trait bound `Point: std::fmt::Display` is not satisfied [--explain E0277]
--> src/main.rs:8:29
8 |> println!("Point 1: {}", p1);
|> ^^
<std macros>:2:27: 2:58: note: in this expansion of format_args!
<std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>)
src/main.rs:8:5: 8:33: note: in this expansion of println! (defined in <std macros>)
note: `Point` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
note: required by `std::fmt::Display::fmt`
```
Whew! The core of the error is this part: *the trait bound `Point:
std::fmt::Display` is not satisfied*. `println!` can do many kinds of
formatting. By default, `{}` implements a kind of formatting known as
`Display`: output intended for direct end-user consumption. The primitive types
weve seen implement `Display`, as theres only one way youd show a `1` to a
user. But with structs, the output is less clear. Do you want commas or not?
What about the `{}`s? Should all the fields be shown?
More complex types in the standard library and that are defined by the
programmer do not automatically implement `Display` formatting. Standard
library types implement `Debug` formatting, which is intended for the
programmer to see. The `#[derive(Debug)]` annotation lets us use a default
implementation of `Debug` formatting to easily get this ability for types we've
defined. To ask `println!` to use `Debug` formatting with our `Point`, we add
the annotation to derive the trait and include `:?` in the print string, like
this:
```rust
#[derive(Debug)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p1 = Point { x: 0.0, y: 5.0};
println!("Point 1: {:?}", p1);
}
```
If you run this, it should print the values of each field in the `Point` struct
as desired:
```bash
$ cargo run
Compiling points v0.1.0 (file:///projects/points)
Running `target/debug/points`
Point 1: Point { x: 0, y: 5 }
```
Youll see this repeated later with other types. Well cover traits fully in
Chapter XX.
## Method Syntax
In the last section on ownership, we made several references to methods.
Methods look like this:
```rust
let s1 = "hello";
// call a method on s1
let s2 = s1.clone();
println!("{}", s1);
```
The call to `clone()` is attached to `s1` with a dot. This is called method
syntax, and its a way to call certain functions with a different style.
Why have two ways to call functions? Well talk about some deeper reasons
related to ownership in a moment, but one big reason is that methods look nicer
when chained together:
```rust,ignore
// with functions
h(g(f(x)));
// with methods
x.f().g().h();
```
The nested-functions version reads in reverse: the program executes `f()`, then
`g()`, then `h()`, but we read it left-to-right as `h()`, then `g()`, then
`f()`. The method syntax is executed in the same order as we would read it.
Before we get into the details, lets talk about how to define your own
methods.
### Defining methods
We can define methods with the `impl` keyword. `impl` is short for
implementation. Doing so looks like this:
```rust
#[derive(Debug,Copy,Clone)]
struct Point {
x: f64,
y: f64,
}
impl Point {
fn distance(&self, other: &Point) -> f64 {
let x_squared = f64::powi(other.x - self.x, 2);
let y_squared = f64::powi(other.y - self.y, 2);
f64::sqrt(x_squared + y_squared)
}
}
let p1 = Point { x: 0.0, y: 0.0 };
let p2 = Point { x: 5.0, y: 6.5 };
assert_eq!(8.200609733428363, p1.distance(&p2));
```
Lets break this down. First, we have our `Point` struct from earlier in the
chapter. Next comes our first use of the `impl` keyword:
```rust,ignore
impl Point {
// ...
}
```
Everything we put inside of the curly braces will be methods implemented on
`Point`. Next is our definition:
```rust,ignore
fn distance(&self, other: &Point) -> f64 {
// ...
}
```
Other than this, the rest of the example is familiar: an implementation of
`distance()` and using the method to find an answer.
Our definition of `distance()` here as a method looks very similar to our
previous definition of `distance()` as a function, but with two differences.
Here's the `distance()` function again:
```rust,ignore
fn distance(p1: Point, p2: Point) -> f64 {
// ...
}
```
The first difference is in the first argument. Instead of a name and a type, we
have written `&self`. This is what distinguishes a method from a function:
using `self` inside of an `impl` block means we have a method. Because we
already know that we are implementing this method on `Point` because of the
surrounding `impl Point` block, we dont need to write the type of `self` out.
Note that we have written `&self`, not just `self`. This is because we want to
take a reference to our argument's value rather than taking ownership of it. In
other words, these two forms are the same:
```rust,ignore
fn foo(self: &Point)
fn foo(&self)
```
Just like any other parameter, you can take `self` in three forms. Heres the
list, with the most common form first:
```rust,ignore
fn foo(&self) // take self by reference
fn foo(&mut self) // take self by mutable reference
fn foo(self) // take self by ownership
```
In this case, we only need a reference. We dont need to mutate either `Point`
to get the distance between them, so we won't take a mutable reference to the
`Point` that we call the method on. Methods that take ownership of `self` are
rarely used. An example of a time to do that would be if we wanted to have a
method that would transform `self` into something else and prevent other code
from using the value of `self` after the transformation happens.
#### Methods and automatic referencing
Weve left out an important detail. Its in this line of the example:
```rust,ignore
assert_eq!(8.200609733428363, p1.distance(&p2));
```
When we defined `distance()`, we took both `self` and the other argument by
reference. Yet, we needed a `&` for `p2` but not `p1`. What gives?
This feature is called automatic referencing, and calling methods is one
of the few places in Rust that has behavior like this. Heres how it works:
when you call a method with `self.(`, Rust will automatically add in `&`s
or `&mut`s to match the signature. In other words, these are the same:
```rust
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
#
# impl Point {
# fn distance(&self, other: &Point) -> f64 {
# let x_squared = f64::powi(other.x - self.x, 2);
# let y_squared = f64::powi(other.y - self.y, 2);
#
# f64::sqrt(x_squared + y_squared)
# }
# }
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
p1.distance(&p2);
(&p1).distance(&p2);
```
The first one looks much, much cleaner. Heres another example:
```rust
let mut s = String::from("Hello,");
s.push_str(" world!");
// The above is the same as:
// (&mut s).push_str(" world!");
assert_eq!("Hello, world!", s);
```
Because [`push_str()`] has the following signature:
```rust,ignore
fn push_str(&mut self, string: &str) {
```
[`push_str()`]: http://doc.rust-lang.org/collections/string/struct.String.html#method.push_str
This automatic referencing behavior works because methods have a clear receiver
— the type of `self` — and in most cases its clear given the receiver and name
of a method whether the method is just reading (so needs `&self`), mutating (so
`&mut self`), or consuming (so `self`). The fact that Rust makes borrowing
implicit for method receivers is a big part of making ownership ergonomic in
practice.

601
nostarch/chapter06.md Normal file
View File

@ -0,0 +1,601 @@
# Enums
Next, lets look at *enumerations*, which allow you to define a type by
enumerating its possible values. Commonly called "enums", these unlock a lot of
power in Rust when combined with pattern matching. Enums are a feature that are
in many languages, but what they can do is different per-language. Rusts enums
are most similar to "algebraic data types" in functional languages like F#,
OCaml, or Haskell.
Heres an example of an enum definition:
```rust
enum IpAddrKind {
V4,
V6,
}
```
This enum represents the kind of an IP address. There are two major standards
used for IP addresses: version four and version six. Any IP address can be
either a version four address or a version six address, but it cannot be both
kinds at the same time. This is where enums get their name: they allow us to
enumerate all of the possible kinds that our value can have.
We can create values of `IpAddrKind` like this:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
```
Note that the variants of the enum are namespaced under its name, and we use
the double colon to separate the two.
Enums have more tricks up their sleeves, however. Thinking more about our IP
address type, we dont have a way to store the actual data of the IP address;
we only know what kind it is. Given that you just learned about structs, you
might tackle this problem like this:
```rust
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
```
Weve used a struct to bundle the two values together: now we keep the kind
with the value itself. We can represent the same thing in a different way with
just an enum:
```rust
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
```
We can attach data to each variant of the enum directly. No need for an extra
struct. But beyond that, this approach is better than using a struct alongside
our enum because we can attach different kinds of data to each variant.
Imagine that instead of a `String`, we would prefer to store a `V4` as its four
individual components while leaving the `V6` variant as a `String`. With our
struct, wed be stuck. But enums deal with this case with ease:
```rust
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
```
You can put any kind of data inside of an enum variant, including another enum!
The `IpAddr` enum is [in the standard library][IpAddr], but it embeds two
different structs inside of its variants:
```rust
struct Ipv4Addr {
// details elided
}
struct Ipv6Addr {
// details elided
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
```
[IpAddr]: http://doc.rust-lang.org/std/net/enum.IpAddr.html
Heres an enum with a variety of types embedded in its variants:
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
```
* `Quit` has no data associated with it at all.
* `Move` includes an anonymous struct inside of it.
* `Write` includes a single `String`.
* `ChangeColor` includes three `i32`s.
This might seem overwhelming, but another way to look at the different enum
possibilities is that they are just like different kinds of struct definitions
that you already know, except without the `struct` keyword and they are grouped
together under the `Message` type. These structs could hold the same data that
these enum variants hold:
```
struct QuitMessage; // unit struct
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct
```
Let's look at another enum in the standard library that is very common and
useful: `Option`.
## Option
Now that we have had an introduction to enums, let's combine them with a
feature that we talked a little bit about in the previous chapter: generics.
Programming language design is often thought of as which features you include,
but it's also about which features you leave out. Rust does not have a feature
that is in many other languages: 'null'. In languages with this feature,
variables can have two states: null or not-null.
The inventor of this concept has this to say:
> I call it my billion-dollar mistake. At that time, I was designing the first
> comprehensive type system for references in an object-oriented language. My
> goal was to ensure that all use of references should be absolutely safe, with
> checking performed automatically by the compiler. But I couldn't resist the
> temptation to put in a null reference, simply because it was so easy to
> implement. This has led to innumerable errors, vulnerabilities, and system
> crashes, which have probably caused a billion dollars of pain and damage in
> the last forty years.
>
> - Tony Hoare "Null References: The Billion Dollar Mistake"
The problem with null values is twofold: first, a value can be null or not, at
any time. The second is that if you try to use a value that's null, you'll get
an error of some kind, depending on the language. Because this property is
pervasive, it's extremely easy to make this kind of error.
Even with these problems, the concept that null is trying to express is still a
useful one: this is a value which is currently invalid or not present for some
reason. The problem isn't with the concept itself, but with the particular
implementation. As such, Rust does not have the concept of null, but we do have
an enum which can encode the concept of a value being present or not present. We
call this enum `Option<T>`, and it looks like this:
```rust
enum Option<T> {
Some(T),
None,
}
```
This enum is [provided by the standard library][option], and is so useful that
it's even in the prelude; you don't need to import it explicitly. Furthermore,
so are its variants: you can say `Some` and `None` directly, without prefixing
them with `Option::`.
[option]: ../std/option/enum.Option.html
Here's an example of using `Option<T>`:
```rust
let some_number = Some(5);
let some_string = Some("a string");
// If we only say None, we need to tell Rust what type of Option<T> we have.
let absent_number: Option<i32> = None;
```
Let's dig in. First, you'll notice that we used the `<T>` syntax when defining
`Option<T>`: it's a generic enum. `Option<T>` has two variants: `Some`, which
contains a `T`, and `None`, which has no data associated with it. In some
sense, `None` means 'null', and `Some` means 'not null'. So why is this any
better than null?
In short, because `Option<T>` and `T` are different types. That's a bit too
short though. Here's an example:
```rust,ignore
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
```
This will not compile. We get an error message like this:
```text
error: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is not
satisfied [E0277]
let sum = x + y;
^~~~~
```
Intense! What this error message is trying to say is that Rust does not
understand how to add an `Option<T>` and a `T`. They're different types! This
shows one of the big advantages of an `Option<T>`: if you have a value that
may or may not exist, you have to deal with that fact before you can assume it
exists. In other words, you have to convert an `Option<T>` to a `T` before you
can do `T` stuff with it. This helps catch one of the most common issues with
null, generally: assuming that something isn't null when it actually is.
This is pretty powerful: in order to have a value that can possibly be null,
you have to explicitly opt in by making the type of that value an `Option<T>`.
Then, when you use that value, you are required to explicitly handle the case
when the value is null. Everywhere that a value has a type that isn't an
`Option<T>`, you *can* safely assume that the value isn't null. This was a
deliberate design decision for Rust to limit null's pervasiveness and increase
the safety of Rust code.
So, how _do_ you get a `T` from an `Option<T>`? The `Option<T>` enum has a
large number of methods that you can check out in [its documentation], and
becoming familiar with them will be extremely useful in your journey with Rust.
[its documentation]: ../std/option/enum.Option.html
But we want a deeper understanding than that. If we didn't have those methods
defined for us already, what would we do? And more generally, how do we get
the inner values out of any enum variant? We need a new feature: `match`.
## Match
Rust has an extremely powerful control-flow operator: `match`. It allows us to
compare a value against a series of patterns and then execute code based on
how they compare.
Think of a `match` expression kind of like a coin sorting machine. Coins slide
down a track that has variously sized holes along it, and each coin falls
through the first hole it encounters that it fits into. In the same way, values
go through each pattern in a `match`, and for the first pattern that the value
"fits", the value will fall into the associated code block to be used during
execution.
Since we're already talking about coins, let's use them for an example using
`match`! We can write a function that can take an unknown American coin and, in
a similar way as the coin counting machine, determine which coin it is and
return its value in cents:
```rust
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> i32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
```
Let's break down the `match`! At a high-level, using `match` looks like this:
```text
match expression {
pattern => code,
}
```
First, we have the `match` keyword. Next, we have an expression. This feels
very similar to an expression used with `if`, but there's a big difference:
with `if`, the condition needs to return a boolean value. Here, it can be any
type.
Next, we have a "match arm". That's the part that looks like `pattern =>
code,`. We can have as many arms as we need to: our `match` above has four
arms. An arm has two parts: a pattern and some code. When the `match`
expression executes, it compares the resulting value against the pattern of
each arm, in order. If a pattern matches the value, the code associated
with that pattern is executed. If that pattern doesn't match the value,
execution continues to the next arm, much like a coin sorting machine.
The code associated with each arm is an expression, and the resulting value of
the code with the matching arm that gets executed is the value that gets
returned for the entire `match` expression.
Curly braces typically aren't used if the match arm code is short, as it is in
the above example where each arm just returns a value. If we wanted to run
multiple lines of code in a match arm, we can use curly braces. This code would
print out "Lucky penny!" every time the method was called with a `Coin::Penny`,
but would still return the last value of the block, `1`:
```rust
# enum Coin {
# Penny,
# Nickel,
# Dime,
# Quarter,
# }
#
fn value_in_cents(coin: Coin) -> i32 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
},
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
```
Another useful feature of match arms is that they can create bindings to parts
of the values that match the pattern. From 1999 through 2008, the U.S. printed
quarters with different designs for each of the 50 states on one side. The other
coins did not get state designs, so only quarters have this extra attribute. We
can add this information to our `enum` by changing the `Quarter` variant to have
a `State` value:
```rust
enum UsState {
Alabama,
Alaska,
// ... etc
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
```
Let's imagine that a friend of ours is trying to collect all 50 state quarters.
While we sort our loose change by coin type in order to count it, we're going
to call out the name of the state so that if it's one our friend doesn't have
yet, they can add it to their collection.
In the match statement to do this, the quarter case now has a binding, `state`,
that contains the value of the state of that quarter. The binding will only get
created if the coin matches the `Quarter` pattern. Then we can use the binding
in the code for that arm:
```rust
# #[derive(Debug)]
# enum UsState {
# Alabama,
# Alaska,
# }
#
# enum Coin {
# Penny,
# Nickel,
# Dime,
# Quarter(UsState),
# }
#
fn value_in_cents(coin: Coin) -> i32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
},
}
}
```
If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` will
be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each of the
match arms, none of them match until we reach `Coin::Quarter(state)`. At that
point, the binding for `state` will be the value `UsState::Alaska`. We can then
use that binding in the `println!`, thus getting the inner state value out of
the `Coin` enum variant for `Quarter`.
Remember the `Option<T>` type from the previous section, and that we wanted to
be able to get the inner `T` value out of the `Some` case? This will be very
similar! Instead of coins, we will be comparing to other patterns, but the way
that the `match` expression works remains the same as a coin sorting machine in
the way that we look for the first pattern that fits the value.
Let's say that we want to write a function that takes an `Option<i32>`, and
if there's a value inside, add one to it. If there isn't a value inside, we
want to return the `None` value and not attempt to add.
This function is very easy to write, thanks to `match`. It looks like this:
```rust
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
```
Let's examine the first execution of `plus_one()` in more detail. In the above
example, `x` will be `Some(5)`. Let's compare that against each arm:
```text
None => None,
```
Does `Some(5)` match `None`? No, it's the wrong variant. So let's continue.
```text
Some(i) => Some(i + 1),
```
Does `Some(5)` match `Some(i)`? Why yes it does! We have the same variant. The
`i` binds to the value inside of the `Some`, so `i` has the value `5`. Then we
execute the code in that match arm: take `i`, which is `5`, add one to it, and
create a new `Some` value with our total inside.
Now let's consider the second call of `plus_one()`. In this case, `x` is
`None`. We enter the `match`, and compare to the first arm:
```text
None => None,
```
Does `None` match `None`? Yup! There's no value to add to. So we stop and
return the `None` value that is on the right side of the `=>`. We don't
check any other arms since we found one that matched.
Combining `match` and enums together is extremely powerful. You'll see this
pattern a lot in Rust code: `match` against an enum, bind to the data
inside, and then execute code based on it. It's a bit tricky at first, but
once you get used to it, you'll wish you had it in languages that don't support
it. It's consistently a user favorite.
### Matches are exhaustive
There's one other aspect of `match` we didn't talk about. Consider this version
of `plus_one()`:
```rust,ignore
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
}
}
```
A bug! We didn't handle the `None` case. Luckily, it's a bug Rust knows how to
catch. If we try to compile this code, we'll get an error:
```text
error: non-exhaustive patterns: `None` not covered [E0004]
match x {
Some(i) => Some(i + 1),
}
```
Rust knows that we did not cover every possible option, and even knows which
pattern we forgot! This is referred to as being "exhaustive": we must exhaust
every last option possible in order to be valid. Especially in the case of
`Option<T>`, when Rust prevents us from forgetting to explicitly handle the
`None` case, it protects us from assuming that we have a value when we might
have null and thus making the billion-dollar mistake we discussed in the
previous section.
### The _ placeholder
What if we don't care about all of the possible values, though? Especially when
there are a lot of possible values for a type: a `u8` can have valid values of
zero through 255-- if we only care about 1, 3, 5, and 7, does this mean we must
list out 0, 2, 4, 6, 8, 9, all the way up through 255? Thankfully, no! We can
use a special pattern, `_`:
```rust
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}
```
The `_` pattern will match all the other cases, and `()` will do nothing, it's
the unit value. This way, we don't have to list individual match arms for all
the other possible values in order to say that we want to do nothing for all of
those-- the `_` is a placeholder for any value.
## if let
There's one more advanced control flow structure we haven't discussed: `if
let`. Imagine we're in a situation like this:
```rust
# let some_option = Some(5);
match some_option {
Some(x) => {
// do something with x
},
None => {},
}
```
We care about the `Some` case, but don't want to do anything with the `None`
case. With an `Option`, this isn't _too_ bad, but with a more complex enum,
adding `_ => {}` after processing just one variant doesn't feel great. We have
this boilerplate arm and an extra level of indentation (the code that
does something with `x` is indented twice, rather than just once). We really want
a construct that says "Do something with this one case; I don't care about the
others."
Enter `if let`:
```rust
# let some_option = Some(5);
if let Some(x) = some_option {
// do something with x
}
```
`if let` takes a pattern and an expression, separated by an `=`. It works
exactly like a `match`, where the expression is given to the `match` and the
pattern is its first arm. In other words, you can think of `if let` as syntax
sugar:
```rust,ignore
if let pattern = expression {
body
}
match expression {
pattern => body,
_ => {}
}
```
And in fact, we can include an `else` and it becomes the body of the `_`
case:
```rust,ignore
if let pattern = expression {
body
} else {
else_body
}
match expression {
pattern => body,
_ => else_body,
}
```
In other words, it's the high-level construct we were originally looking for:
do something special with only one pattern.

File diff suppressed because it is too large Load Diff

View File

@ -1,697 +0,0 @@
# Structs
So far, all of the data types weve seen allow us to have a single value
at a time. `struct`s give us the ability to package up multiple values and
keep them in one related structure.
Lets write a program which calculates the distance between two points.
Well start off with single variable bindings, and then refactor it to
use `struct`s instead.
Lets make a new project with Cargo:
```bash
$ cargo new --bin points
$ cd points
```
Heres a short program which calculates the distance between two points. Put
it into your `src/main.rs`:
```rust
fn main() {
let x1 = 0.0;
let y1 = 5.0;
let x2 = 12.0;
let y2 = 0.0;
let answer = distance(x1, y1, x2, y2);
println!("Point 1: ({}, {})", x1, y1);
println!("Point 2: ({}, {})", x2, y2);
println!("Distance: {}", answer);
}
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
let x_squared = f64::powi(x2 - x1, 2);
let y_squared = f64::powi(y2 - y1, 2);
f64::sqrt(x_squared + y_squared)
}
```
Let's try running this program with `cargo run`:
```bash
$ cargo run
Compiling points v0.1.0 (file:///projects/points)
Running `target/debug/points`
Point 1: (0, 5)
Point 2: (12, 0)
Distance: 13
```
Let's take a quick look at `distance()` before we move forward:
```rust
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
let x_squared = f64::powi(x2 - x1, 2);
let y_squared = f64::powi(y2 - y1, 2);
f64::sqrt(x_squared + y_squared)
}
```
To find the distance between two points, we can use the Pythagorean Theorem.
The theorem is named after Pythagoras, who was the first person to mathematically
prove this formula. The details aren't that important, to be honest. There's a few
things that we haven't discussed yet, though.
```rust,ignore
f64::powi(2.0, 3)
```
The double colon (`::`) here is a namespace operator. We havent talked about
modules yet, but you can think of the `powi()` function as being scoped inside
of another name. In this case, the name is `f64`, the same as the type. The
`powi()` function takes two arguments: the first is a number, and the second is
the power that it raises that number to. In this case, the second number is an
integer, hence the i in its name. Similarly, `sqrt()` is a function under the
`f64` module, which takes the square root of its argument.
## Why `struct`s?
Our little program is okay, but we can do better. The key is in the signature
of `distance()`:
```rust,ignore
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
```
The distance function is supposed to calculate the distance between two points.
But our distance function calculates some distance between four numbers. The
first two and last two arguments are related, but thats not expressed anywhere
in our program itself. We need a way to group `(x1, y1)` and `(x2, y2)`
together.
Weve already discussed one way to do that: tuples. Heres a version of our program
which uses tuples:
```rust
fn main() {
let p1 = (0.0, 5.0);
let p2 = (12.0, 0.0);
let answer = distance(p1, p2);
println!("Point 1: {:?}", p1);
println!("Point 2: {:?}", p2);
println!("Distance: {}", answer);
}
fn distance(p1: (f64, f64), p2: (f64, f64)) -> f64 {
let x_squared = f64::powi(p2.0 - p1.0, 2);
let y_squared = f64::powi(p2.1 - p1.1, 2);
f64::sqrt(x_squared + y_squared)
}
```
This is a little better, for sure. Tuples let us add a little bit of structure.
Were now passing two arguments, so thats more clear. But its also worse.
Tuples dont give names to their elements, and so our calculation has gotten
much more confusing:
```rust,ignore
p2.0 - p1.0
p2.1 - p1.1
```
When writing this example, your authors almost got it wrong themselves! Distance
is all about `x` and `y` points, but now its all about `0` and `1`. This isnt
great.
Enter `struct`s. We can transform our tuples into something with a name:
```rust,ignore
let p1 = (0.0, 5.0);
struct Point {
x: f64,
y: f64,
}
let p1 = Point { x: 0.0, y: 5.0 };
```
Heres what declaring a `struct` looks like:
```text
struct NAME {
NAME: TYPE,
}
```
The `NAME: TYPE` bit is called a field, and we can have as many or as few of
them as youd like. If you have none of them, drop the `{}`s:
```rust
struct Foo;
```
`struct`s with no fields are called unit structs, and are used in certain
advanced situations. We will just ignore them for now.
You can access the field of a struct in the same way you access an element of
a tuple, except you use its name:
```rust,ignore
let p1 = (0.0, 5.0);
let x = p1.0;
struct Point {
x: f64,
y: f64,
}
let p1 = Point { x: 0.0, y: 5.0 };
let x = p1.x;
```
Lets convert our program to use our `Point` `struct`. Heres what it looks
like now:
```rust
#[derive(Debug,Copy,Clone)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p1 = Point { x: 0.0, y: 5.0};
let p2 = Point { x: 12.0, y: 0.0};
let answer = distance(p1, p2);
println!("Point 1: {:?}", p1);
println!("Point 2: {:?}", p2);
println!("Distance: {}", answer);
}
fn distance(p1: Point, p2: Point) -> f64 {
let x_squared = f64::powi(p2.x - p1.x, 2);
let y_squared = f64::powi(p2.y - p1.y, 2);
f64::sqrt(x_squared + y_squared)
}
```
Our function signature for `distance()` now says exactly what we mean: it
calculates the distance between two `Point`s. And rather than `0` and `1`,
weve got back our `x` and `y`. This is a win for clarity.
Theres one other thing thats a bit strange here, this annotation on our
`struct` declaration:
```rust,ignore
#[derive(Debug,Copy,Clone)]
struct Point {
```
We havent yet talked about traits, but we did talk about `Debug` when we
discussed arrays. This `derive` attribute allows us to tweak the behavior of
our `Point`. In this case, we are opting into copy semantics, and everything
that implements `Copy` must implement `Clone`.
# Method Syntax
In the last section on ownership, we made several references to methods.
Methods look like this:
```rust
let s1 = String::from("hello");
// call a method on our String
let s2 = s1.clone();
println!("{}", s1);
```
The call to `clone()` is attatched to `s1` with a dot. This is called method
syntax, and its a way to call certain functions with a different style.
Why have two ways to call functions? Well talk about some deeper reasons
related to ownership in a moment, but one big reason is that methods look nicer
when chained together:
```rust,ignore
// with functions
h(g(f(x)));
// with methods
x.f().g().h();
```
The nested-functions version reads in reverse: we call `f()`, then `g()`, then
`h()`, but it reads as `h()`, then `g()`, then `f()`.
Before we get into the details, lets talk about how to define your own
methods.
## Defining methods
We can define methods with the `impl` keyword. `impl` is short for
implementation. Doing so looks like this:
```rust
#[derive(Debug,Copy,Clone)]
struct Point {
x: f64,
y: f64,
}
impl Point {
fn distance(&self, other: &Point) -> f64 {
let x_squared = f64::powi(other.x - self.x, 2);
let y_squared = f64::powi(other.y - self.y, 2);
f64::sqrt(x_squared + y_squared)
}
}
let p1 = Point { x: 0.0, y: 0.0 };
let p2 = Point { x: 5.0, y: 6.5 };
assert_eq!(8.200609733428363, p1.distance(&p2));
```
Lets break this down. First, we have our `Point` struct from earlier in the
chapter. Next comes our first use of the `impl` keyword:
```
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
#
impl Point {
# fn distance(&self, other: &Point) -> f64 {
# let x_squared = f64::powi(other.x - self.x, 2);
# let y_squared = f64::powi(other.y - self.y, 2);
#
# f64::sqrt(x_squared + y_squared)
# }
}
#
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
#
# assert_eq!(8.200609733428363, p1.distance(&p2));
```
Everything we put inside of the curly braces will be methods implemented on
`Point`.
```
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
#
# impl Point {
fn distance(&self, other: &Point) -> f64 {
# let x_squared = f64::powi(other.x - self.x, 2);
# let y_squared = f64::powi(other.y - self.y, 2);
#
# f64::sqrt(x_squared + y_squared)
}
# }
#
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
#
# assert_eq!(8.200609733428363, p1.distance(&p2));
```
Next is our definition. This looks very similar to our previous definition of
`distance()` as a function:
```rust
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
fn distance(p1: Point, p2: Point) -> f64 {
# let x_squared = f64::powi(p2.x - p1.x, 2);
# let y_squared = f64::powi(p2.y - p1.y, 2);
#
# f64::sqrt(x_squared + y_squared)
# }
```
Other than this, the rest of the example is familliar: an implementation of
`distance()`, and using the method to find an answer.
There are two differences. The first is in the first argument. Instead of a name
and a type, we have written `&self`. This is what distinguishes a method from a
function: using `self` inside of an `impl` block. Because we already know that
we are implementing this method on `Point`, we dont need to write the type of
`self` out. However, we have written `&self`, not only `self`. This is because
we want to take our argument by reference rather than by ownership. In other
words, these two forms are the same:
```rust,ignore
fn foo(self: &Point)
fn foo(&self)
```
Just like any other parameter, you can take `self` in three forms. Heres the
list, with the most common form first:
```rust,ignore
fn foo(&self) // take self by reference
fn foo(&mut self) // take self by mutable reference
fn foo(self) // take self by ownership
```
In this case, we only need a reference. We dont plan on taking ownership, and
we dont need to mutate either point. Taking by reference is by far the most
common form of method, followed by a mutable reference, and then occasionally
by ownership.
### Methods and automatic referencing
Weve left out an important detail. Its in this line of the example:
```
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
#
# impl Point {
# fn distance(&self, other: &Point) -> f64 {
# let x_squared = f64::powi(other.x - self.x, 2);
# let y_squared = f64::powi(other.y - self.y, 2);
#
# f64::sqrt(x_squared + y_squared)
# }
# }
#
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
#
assert_eq!(8.200609733428363, p1.distance(&p2));
```
When we defined `distance()`, we took both `self` and the other argument by
reference. Yet, we needed a `&` for `p2` but not `p1`. What gives?
This feature is called automatic referencing, and calling methods is one
of the few places in Rust that has behavior like this. Heres how it works:
when you call a method with `self.(`, Rust will automatically add in `&`s
or `&mut`s to match the signature. In other words, these three are the same:
```rust
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
#
# impl Point {
# fn distance(&self, other: &Point) -> f64 {
# let x_squared = f64::powi(other.x - self.x, 2);
# let y_squared = f64::powi(other.y - self.y, 2);
#
# f64::sqrt(x_squared + y_squared)
# }
# }
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
p1.distance(&p2);
(&p1).distance(&p2);
Point::distance(&p1, &p2);
```
The first one looks much, much cleaner. Heres another example:
```rust
let mut s = String::from("Hello,");
s.push_str(" world!");
// The above is the same as:
// (&mut s).push_str(" world!");
assert_eq!("Hello, world!", s);
```
Because [`push_str()`] has the following signature:
```rust,ignore
fn push_str(&mut self, string: &str) {
```
[`push_str()`]: http://doc.rust-lang.org/collections/string/struct.String.html#method.push_str
This automatic referencing behavior works because methods have a clear receiver
— the type of `self` — and in most cases its clear given the receiver and name
of a method whether the method is just reading (so needs `&self`), mutating (so
`&mut self`), or consuming (so `self`). The fact that Rust makes borrowing
implicit for method receivers is a big part of making ownership ergonomic in
practice.
## Methods can be called like functions
Furthermore, if we have a method, we can also call it like a function:
```rust
# #[derive(Debug,Copy,Clone)]
# struct Point {
# x: f64,
# y: f64,
# }
#
# impl Point {
# fn distance(&self, other: &Point) -> f64 {
# let x_squared = f64::powi(other.x - self.x, 2);
# let y_squared = f64::powi(other.y - self.y, 2);
#
# f64::sqrt(x_squared + y_squared)
# }
# }
# let p1 = Point { x: 0.0, y: 0.0 };
# let p2 = Point { x: 5.0, y: 6.5 };
let d1 = p1.distance(&p2);
let d2 = Point::distance(&p1, &p2);
assert_eq!(d1, d2);
```
Instead of using `self.(`, we use `Point` and the namespace operator to call it
like a function instead. Because functions do not do the automatic referencing,
we must pass in `&p1` explicitly.
While methods can be called like functions, functions cannot be called like
methods. If the first argument isnt named `self`, it cannot be called like a
method.
# Generics
We've been working with a `Point` struct that looks like this:
```rust
#[derive(Debug,Copy,Clone)]
struct Point {
x: f64,
y: f64,
}
```
But what if we didn't want to always use an `f64` here? What about an `f32` for
when we need less precision? Or an `i32` if we only want integer coordinates?
While our simple `Point` struct may be a bit too simple to bother making
generic in a real application, we're going to stick with it to show you the
syntax. Especially when building library code, generics allow for more code
re-use, and unlock a lot of powerful techniques.
## Generic data types
'Generics' let us write code that allows for several different types, while
letting us have one definition. A more generic `Point` would look like this:
```rust
#[derive(Debug,Copy,Clone)]
struct Point<T> {
x: T,
y: T,
}
```
There are two changes here, and they both involve this new `T`. The first change
is in the definition:
```rust
# #[derive(Debug,Copy,Clone)]
struct Point<T> {
# x: T,
# y: T,
# }
```
Our previous definition said, "We are defining a struct named Point." This
definition says something slightly different: "We are defining a struct named
Point with one type parameter `T`."
Let's talk about this term 'type parameter'. We've already seen one other thing
called a 'parameter' in Rust: function parameters:
```rust
fn plus_one(x: i32) -> i32 {
x + 1
}
```
Here, `x` is a parameter to this function. We can call this function with a
different value, and `x` will change each time it's called:
```rust
# fn plus_one(x: i32) -> i32 {
# x + 1
# }
let six = plus_one(5);
let eleven = plus_one(10);
```
In the same way, a type parameter allows us to define a data type which can be
different each time we use it:
```rust
#[derive(Debug,Copy,Clone)]
struct Point<T> {
x: T,
y: T,
}
let integral_point = Point { x: 5, y: 5 };
let floating_point = Point { x: 5.0, y: 5.0 };
```
Here, `integral_point` uses `i32` values for `T`, and `floating_point` uses
`f64` values. This also leads us to talk about the second change we made to `Point`:
```rust
# #[derive(Debug,Copy,Clone)]
# struct Point<T> {
x: T,
y: T,
# }
```
Instead of saying `x: i32`, we say `x: T`. This `T` is the same one that we
used above in the struct declaration. Because `x` and `y` both use `T`, they'll
be the same type. We could give them different types:
```rust
#[derive(Debug,Copy,Clone)]
struct Point<T, OtherT> {
x: T,
y: OtherT,
}
let different = Point { x: 5, y: 5.0 };
let same = Point { x: 5.0, y: 5.0 };
```
Here, instead of a single parameter, `T`, we have two: `T` and `OtherT`. Type
parameters have the same naming convention as other types: `CamelCase`.
However, you'll often see short, one-letter names used for types. `T` is very
common, because it's short for 'type', but you can name them something longer
if you'd like. In this version of `Point`, we say that `x` has the type `T`,
and `y` has the type `OtherT`. This lets us give them two different types, but
they don't have to be.
## Generic functions
Regular old functions can also take generic parameters, with a syntax that looks
very similar:
```rust
fn foo<T>(x: T) {
// ...
}
```
This `foo()` function has one generic parameter, `T`, and takes one argument,
`x`, which has the type `T`. Let's talk a little bit more about what this means.
## Generic methods
We've seen how to define methods with the `impl` keyword. Our generic `Point`
can have generic methods, too:
```rust
#[derive(Debug,Copy,Clone)]
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn some_method(&self) {
// ...
}
}
```
We also need the `<T>` after `impl`. This line reads, "We will be implementing
methods with one generic type parameter, `T`, for a type, `Point`, which takes
one generic type `T`." In a sense, the `impl<T>` says "we will be using a type
`T`" and the `Point<T>` says "that `T` is used for `Point`." In this simple
case, this syntax can feel a bit redundant, but when we get into some of Rust's
more advanced features later, this distinction will become more useful.
## There's more to the story
This section covered the basic syntax of generics, but it's not the full story.
For example, let's try to implement our `foo()` function: we'll have it print out
the value of `x`:
```rust,ignore
fn foo<T>(x: T) {
println!("x is: {}", x);
}
```
We'll get an error:
```text
error: the trait `core::fmt::Display` is not implemented for the type `T` [E0277]
println!("x is: {}", x);
^
```
We can't print out `x`! The error messages reference something we talked about
breifly before, the `Display` trait. In order to implement this function, we
need to talk about traits. But we only need to talk about traits to implement
our own generic functions; we don't need this understanding to use them. So
rather than get into more details about this right now, let's talk about other
useful Rust data types, and we can come back to implementing generic functions
in the chapter about traits.
For now, the important bits to understand:
* Generic type parameters are kind of like function parameters, but for types
instead of values.
* Type parameters go inside `<>`s and are usually named things like `T`.
With that, let's talk about another fundamental Rust data type: enums.

View File

@ -1,738 +0,0 @@
# Enums
Next, lets look at a feature of Rust thats similar to structs, but also
different. Enumerations, or enums as theyre more commonly referred to,
are an extremely powerful feature of Rust. Enums are a feature that are in many
languages, but what they can do is different per-language. Rusts enums are
most similar to enums in functional languages.
Heres an example of an enum:
```rust
enum IpAddrKind {
V4,
V6,
}
```
This enum represents the kind of an IP address. There are two major standards
used for IP addresses: version four, and version six. Any IP address can be either
a version four address, or a version six address. But it cannot be both kinds at
the same time. This is where enums get their name: they allow us to enumerate all
of the possible kinds that our value can have.
We can create values of `IpAddrKind` like this:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
```
Note that the variants of the enum are namespaced under its name, and we use
the double colon to separate the two.
Enums have more tricks up their sleeves, however. Thinking more about our IP
address type, we dont have a way to store the actual data of the IP address,
we only know what kind it is. Given that you just learned about structs, you
might tackle this problem like this:
```rust
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
```
Weve used a struct to bundle the two values together: now we keep the kind
with the value itself. This design isnt bad, exactly, but it wouldnt be
considered idiomatic Rust. We can represent the same thing with just an enum:
```rust
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
```
We can attach data to each variant of the enum directly. No need for an extra
struct. But beyond that, this approach is better than using a struct alongside
our enum because we can attatch different kinds of data to each variant.
Imagine that instead of a `String`, we would prefer to store a `V4` as its four
individual components, while leaving the `V6` variant as a `String`. With our
struct, wed be stuck. But enums deal with this case with ease:
```rust
enum IpAddr {
V4(u32, u32, u32, u32),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
```
You can put any kind of data inside of an enum variant, including another enum!
The `IpAddr` enum is [in the standard library][IpAddr], but it embeds two different
structs inside of its variants:
```rust
struct Ipv4Addr {
// details elided
}
struct Ipv6Addr {
// details elided
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
```
[IpAddr]: http://doc.rust-lang.org/std/net/enum.IpAddr.html
Heres an enum with a variety of types embedded in its variants:
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
```
* `Quit` has no data associated with it at all.
* `Move` includes an anonymous struct inside of it.
* `Write` includes a single `String`.
* `ChangeColor` includes three `i32`s.
We havent talked a lot about how to access the data inside an enum variant,
however. To do that, lets move on to some new Rust syntax thats especially
useful with enums: `match`.
# Option
Now that we have a handle on enums, let's combine them with a feature that we
talked a little bit about in the previous chapter: generics.
Programming language design is often though of as which features you include,
but it's also about which features you leave out. Rust does not have a feature
that is in many other languages: 'null'. In languages with this feature,
variables can have two states: null or not-null.
The inventor of this concept has this to say:
> I call it my billion-dollar mistake. At that time, I was designing the first
> comprehensive type system for references in an object-oriented language. My
> goal was to ensure that all use of references should be absolutely safe, with
> checking performed automatically by the compiler. But I couldn't resist the
> temptation to put in a null reference, simply because it was so easy to
> implement. This has led to innumerable errors, vulnerabilities, and system
> crashes, which have probably caused a billion dollars of pain and damage in
> the last forty years.
>
> - Tony Hoare "Null References: The Billion Dollar Mistake"
The problem with null values is twofold: first, a value can be null or not, at
any time. The second is that if you try to use a value that's null, you'll get
an error of some kind, depending on the language. Because this property is
pervasive, it's extremely easy to make this kind of error.
Even with these problems, the concept that null is trying to express is still a
useful one: this is a value which is currently invalid or not present for some
reason. The problem isn't with the concept itself, but with the particular
implementation. As such, Rust does not have the concept of null, but we do have
a type which can encode the concept of a value being present. We call this type
`Option<T>`, and it looks like this:
```rust
enum Option<T> {
Some(T),
None,
}
```
This type is [provided by the standard library][option], and is so useful that
it's even in the prelude; you don't need to import it explicitly. Furthermore,
so are its variants: you can say `Some` and `None` directly, without prefixing
them with `Option::`.
[option]: ../std/option/enum.Option.html
Here's an example of using `Option<T>`:
```rust
let some_number = Some(5);
let some_string = Some("a string");
// If we only say None, we need to tell Rust what type of Option<T> we have.
let absent_number: Option<i32> = None;
```
Let's dig in. First, you'll notice that we used the `<T>` syntax when defining
`Option<T>`: it's a generic enum. `Option<T>` has two variants: `Some`, which
contains a `T`, and `None`, which has no data associated with it. In some
sense, `None` means 'null', and `Some` means 'not null'. So why is this any
better than null?
In short, because `Option<T>` and `T` are different types. That's a bit too
short though. Here's an example:
```rust,ignore
let x = 5;
let y = Some(5);
let sum = x + y;
```
This will not compile. We get an error message like this:
```text
error: the trait `core::ops::Add<core::option::Option<_>>` is not implemented
for the type `_` [E0277]
let sum = x + y;
^~~~~
```
Intense! What this error message is trying to say is that Rust does not
understand how to add an `Option<T>` and a `T`. They're different types! This
shows one of the big advantages of an `Option<T>` type: if you have a type that
may or may not exist, you have to deal with that fact before you can assume it
exists. In other words, you have to convert an `Option<T>` to a `T` before you
can do `T` stuff with it. This helps catch one of the most common issues with
null, generally: assuming that something isn't null, when it actually is.
So, how _do_ you get a `T` from an `Option<T>`? The option type has a large
number of methods that you can check out in [its documentation], and becoming
familiar with them will be extremely useful in your journey with Rust.
[its documentation]: ../std/option/enum.Option.html
But we want a deeper understanding than that. If we didn't have those methods
defined for us already, what would we do? For that, we need a new feature: `match`.
# Match
Rust has an extremely powerful control-flow operator: `match`. It allows us to
compare a value against a series of patterns, and then execute code based on
how they compare. Remember the `Option<T>` type from the previous section?
Let's say that we want to write a function that takes an `Option<i32>`, and
if there's a value inside, add one to it.
This function is very easy to write, thanks to `match`. It looks like this:
```rust
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
```
Let's break down the `match`! At a high-level, the `match` expression looks
like this:
```text
match condition {
pattern => code,
}
```
First, we have the `match` keyword. Next, we have a condition. This feels very
similar to an `if` expression, but there's a big difference: with `if`, the
condition needs to be a boolean. Here, it can be any type.
Next, we have a "match arm". That's the part that looks like `pattern =>
code,`. We can have as many arms as we need to: our `match` above has two
arms. An arm has two parts: a pattern, and some code. When the `match`
expression executes, it compares the condition against the pattern of each arm,
in turn. If the pattern matches the condition, the associated code is executed,
and the rest of the patterns are not checked. If it doesn't match, execution
continues to the next arm.
Let's examine the first execution of `plus_one()` in more detail. In the above
example, `x` will be `Some(5)`. Let's compare that against each arm:
```text
None => None,
```
Does `Some(5)` match `None`? No, it's the wrong variant. So let's continue.
```text
Some(i) => Some(i + 1),
```
Does `Some(5)` match `Some(i)`? Why yes it does! We have the same variant. But
what about `i`? In a pattern like this, we can declare new bindings, similarly
to what we did with `let`. So in this case, the code part of the match arm will
have a binding, `i`, which corresponds to the `5`.
With this arm, the code portion is `Some(i + 1)`. So we do exactly that: we
take `i`, which is `5`, add one to it, and create a new `Some` value with our
sum inside.
Because `match` is an expression, the value of the overall expression becomes
the value of the arm that executed. So the value of this `match` expression
will be `Some(6)`. And since our `match` is the only expression in the
function, the value of the `match` will be the value of the function, and so
`Some(6)` is our return value as well, which is exactly what we were shooting
for.
Now let's consider the second call. In this case, `x` is `None`. We enter the
`match`, and compare to the first arm:
```text
None => None,
```
Does `None` match `None`? Yup! And so we return `None`. There's no value to add
to.
Combining `match` and enums together is extremely powerful. You'll see this
pattern a lot in Rust code: `match` against an enum, binding to the data
inside, and then executing code based on it. It's a bit tricky at first, but
once you get used to it, you'll wish you had it in languages that don't support
it. It's consistently a user favorite.
## Matches are exhaustive
There's one other aspect of `match` we didn't talk about. Consider this version
of `plus_one()`:
```rust,ignore
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
Some(i) => Some(i + 1),
}
}
```
A bug! We didn't handle the `None` case. Luckily, it's a bug Rust knows how to catch.
If we try to compile this code, we'll get an error:
```text
error: non-exhaustive patterns: `None` not covered [E0004]
match x {
Some(i) => Some(i + 1),
}
```
Rust knows that we did not cover every possible option, and even knows which
pattern we forgot! This is referred to as being "exhaustive", we must exhaust
every last option possible in order to be valid!
This analysis isn't perfect, however. This will also error:
```rust,ignore
# let some_u8_value = 0u8;
match some_u8_value {
0 => println!("zero"),
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
4 => println!("four"),
5 => println!("five"),
6 => println!("six"),
7 => println!("seven"),
// We won't write out all of the arms here, but imagine that there are more
// arms corresponding to the rest of the numbers.
254 => println!("two-hundred and fifty-four"),
255 => println!("two-hundred and fifty-five"),
}
```
Even though a `u8` can only have valid values of zero through 255, Rust isn't
quite smart enough to understand we've covered all the cases. In order to fix
this, we can use a special pattern, `_`:
```rust
# let some_u8_value = 0u8;
match some_u8_value {
0 => println!("zero"),
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
4 => println!("four"),
5 => println!("five"),
6 => println!("six"),
7 => println!("seven"),
// ...
254 => println!("two-hundred and fifty-four"),
255 => println!("two-hundred and fifty-five"),
_ => panic!("can't ever happen"),
}
```
The `_` pattern matches anything at all, and so with it as the final pattern,
Rust can understand that we have all our bases covered. It's not only used for
this sort of exhastiveness issue, though. It's useful any time we don't want to
deal with a number of cases. Consider this scenario: if we wanted to print out
something one one, three, five, and seven:
```rust
# let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}
```
The `_` pattern will match all the other cases, and `()` will do nothing, it's
the unit value.
## More about patterns
As we've just seen, patterns are powerful, yet complex. Let's take a whole
section to cover all of the things that they can do.
# if let
There's one more advanced control flow structure we haven't discussed: `if
let`. Imagine we're in a situation like this:
```rust
# let some_option = Some(5);
match some_option {
Some(x) => {
// do something with x
},
None => {},
}
```
We care about the `Some` case, but don't want to do anything with the `None`
case. With an `Option`, this isn't _too_ bad, but with a more complex enum,
adding `_ => {}` after processing just one variant doesn't feel great. We have
this boilerplate arm, and we have an extra level of indentation: the code that
does something with `x` is indented twice, rather than just once. We really want
a construct that says "Do something with this one case, I don't care about the
others."
Enter `if let`:
```rust
# let some_option = Some(5);
if let Some(x) = some_option {
// do something with x
}
```
`if let` takes a pattern and an expression, separated by an `=`. It works
exactly like a `match`, where the expression is given to the `match` and the
pattern is its first arm. In other words, you can think of `if let` as syntax
sugar:
```rust,ignore
if let pattern = expression {
body
}
match expression {
pattern => body,
_ => {}
}
```
And in fact, we can include an `else` and it becomes the body of the `_`
case:
```rust,ignore
if let pattern = expression {
body
} else {
else_body
}
match expression {
pattern => body,
_ => else_body,
}
```
In other words, it's the high-level construct we were originally looking for:
do something with a single pattern.
# Patterns
We've mentioned 'patterns' a few times so far: they're used in `let` bindings,
in function arguments, and in the `match` expression. Patterns have a lot of
abilities, so in this section, we'll cover all of the different things they can
do. Any of these abilities work in any place where a pattern is used.
## Literals & _
You can match against literals directly, and `_` acts as an ‘any’ case:
```rust
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
```
This prints `one`.
# Multiple patterns
You can match multiple patterns with `|`:
```rust
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
```
This prints `one or two`.
## ref and ref mut
Usually, when you match against a pattern, bindings are bound by value.
This means you'll end up moving the value out:
```rust,ignore
let name = Some(String::from("Bors"));
match name {
Some(name) => println!("Found a name: {}", name),
None => (),
}
// name is moved here. This line will fail to compile:
println!("name is: {:?}", name);
```
If you'd prefer to bind `name` by reference, use the `ref` keyword:
```rust
let name = Some(String::from("Bors"));
match name {
Some(ref name) => println!("Found a name: {}", name),
None => (),
}
// name is not moved here; the match only took a reference to its data rather
// than moving it. This will work:
println!("name is: {:?}", name);
```
And for a mutable reference, `ref mut`:
```rust
let mut name = Some(String::from("Bors"));
match name {
Some(ref mut name) => *name = String::from("Another name"),
None => (),
}
// name is not moved here; the match only took a reference to its data rather
// than moving it
```
## Destructuring
Patterns can be used to destructure structs and enums:
```rust
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
let Point { x, y } = origin;
```
This brings an `x` and `y` binding into scope, matching the `x` and `y` of
`origin`. While it can be unusual in `let`, this is the same principle of
patterns in `match`:
```rust
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x, y } => { }, // x and y are bound here
}
```
## Shadowing
As with all bindings, anything bound by a pattern will shadow bindings
outside of the binding construct:
```rust
let x = Some(5);
match x {
Some(x) => { }, // x is an i32 here, not an Option<i32>
None => (),
}
```
## Ignoring bindings
We discussed using `_` as a whole pattern to ignore it above, but you can
also use `_` inside of another pattern to ignore just part of it:
```rust
let x = Some(5);
match x {
Some(_) => println!("got a Some and I don't care what's inside"),
None => (),
}
```
Or like this:
```rust
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, _, third, _, fifth) => println!("Some numbers: {}, {}, {}", first, third, fifth),
}
```
If you want, you can use `..` to ignore all of the parts you haven't defined:
```rust
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => { }, // y and z are ignored
}
```
## Ranges
You can match a range of values with `...`:
```rust
let x = 5;
match x {
1 ... 5 => println!("one through five"),
_ => println!("something else"),
}
```
Ranges are usually used with integers or `char`s:
```rust
fn main() {
let x = 'c';
match x {
'a' ... 'j' => println!("early ASCII letter"),
'k' ... 'z' => println!("late ASCII letter"),
_ => println!("something else"),
}
}
```
## Guards
You can introduce ‘match guards’ with `if`:
```rust
let x = Some(5);
match x {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
```
If you’re using if with multiple patterns, the if applies to both sides:
```rust
let x = 4;
let y = false;
match x {
4 | 5 if y => println!("yes"),
_ => println!("no"),
}
```
This prints `no`, because the if applies to the whole of `4 | 5`, and not to only
the `5`. In other words, the precedence of if behaves like this:
```text
(4 | 5) if y => ...
```
not this:
```text
4 | (5 if y) => ...
```
## Bindings
You can bind values to names with `@`: