From cf39be045fc8e773b21a9e0a5f3ac9496ca91176 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 14 Oct 2016 14:14:45 -0400 Subject: [PATCH] Ship new version of ch 2 to nostarch --- nostarch/chapter02.md | 804 ++++++++++++++++++++++-------------------- 1 file changed, 413 insertions(+), 391 deletions(-) diff --git a/nostarch/chapter02.md b/nostarch/chapter02.md index ab7c044..df9581c 100644 --- a/nostarch/chapter02.md +++ b/nostarch/chapter02.md @@ -3,32 +3,34 @@ # Guessing Game -Let's jump into Rust with a hands-on project! This chapter will introduce you to -a few common Rust concepts by showing how you would use them in a real program. -You'll learn about `let`, `match`, methods, associated functions, using -external crates, and more! Following chapters will explore these ideas in more -detail. +Let’s jump into Rust by working through a hands-on project together! This +chapter introduces you to a few common Rust concepts by showing you how to use +them in a real program. You’ll learn about `let`, `match`, methods, associated +functions, using external crates, and more! The following chapters will explore +these ideas in more detail. In this chapter, you’ll practice the fundamentals. -We’re going to implement a classic beginner programming problem: the guessing -game. Here’s 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 we’re too low or too high. Once we guess correctly, -it will congratulate us. +We’ll implement a classic beginner programming problem: a guessing game. +Here’s how it works: the program will generate a random integer between 1 and +100. It will then prompt the player to enter a guess. After entering a guess, it +will indicate whether the guess is too low or too high. If the guess is +correct, the game will print congratulations and exit. ## Setting Up a New Project -Let’s set up a new project. Go to your projects directory from the previous -chapter, and create a new project using Cargo, like so: +To set up a new project, go to the projects directory that you created in +Chapter 1, and make a new project using Cargo, like so: ```bash $ cargo new guessing_game --bin $ cd guessing_game ``` -We pass the name of our project to `cargo new` and pass the `--bin` flag, since -we’re going to be making another binary like in Chapter 1. +The first command, `cargo new`, takes the name of the project (`guessing_game`) +as the first argument. The `--bin` flag tells Cargo to make a binary project, +similar to the one in Chapter 1. The second command changes to the new +project's directory. -Take a look at the generated `Cargo.toml`: +Look at the generated `Cargo.toml` file: Filename: Cargo.toml @@ -41,11 +43,11 @@ authors = ["Your Name "] [dependencies] ``` -If the author information that Cargo got from your environment is not correct, -go ahead and fix that in the file and save it again. +If the author information that Cargo obtained from your environment is not +correct, fix that in the file and save it again. -And as we saw in the last chapter, `cargo new` generates a "Hello, world!" -program for us. Check out `src/main.rs`: +As you saw in Chapter 1, `cargo new` generates a “Hello, world!” program for +you. Check out the `src/main.rs` file: Filename: src/main.rs @@ -55,8 +57,8 @@ fn main() { } ``` -Let’s try compiling what Cargo gave us and running it in the same step, using -the `cargo run` command: +Now let’s compile this "Hello, world!" program and run it in the same step +using the `cargo run` command: ```bash $ cargo run @@ -65,19 +67,17 @@ $ cargo run 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. +The `run` command comes in handy when you need to rapidly iterate on a project, +and this 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. We’ll be writing all of our code in this -file. +Reopen the `src/main.rs` file. You’ll be writing all the code in this file. ## Processing a Guess -Let’s get to it! We'll split the development of this game up into parts. This -first part will ask for input from a user and process the input, checking that -the input is in the form we expect. First we need to allow our player to input -a guess. Enter this in your `src/main.rs`: +The first part of the program will ask for user input, process that input, and +check that the input is in the expected form. To start, we’ll allow the player +to input a guess. Enter the code in Listing 2-1 into `src/main.rs`. Filename: src/main.rs @@ -98,29 +98,36 @@ fn main() { } ``` -There’s a lot here! Let’s go over it, bit by bit. + +Listing 2-1: Code to get a guess from the user and print it out + + +This code contains a lot of information, so let’s go over it bit by bit. To +obtain user input and then print the result as output, we need to import the +`io` (input/output) library from the standard library (which is known as `std`): ```rust,ignore use std::io; ``` -We’ll need to take user input and then print the result as output, and for that -functonality we need to import the `io` (input/output) library from the -standard library (which is known as `std`). +By default, Rust imports only a few types into every program in the +*prelude*. If a type you want to use isn’t in the +prelude, you have to import that type into your program explicitly with a `use` +statement. Using the `std::io` library provides you with a number of useful +`io`-related features, including the functionality to accept user input. -By default, Rust only imports a few things into every program in the -*prelude*. If it’s not in the prelude, you’ll have to import it into -your program explicitly with a `use` statement. Using the `std::io` library -gets you a number of useful `io`-related things, including the functionality to -accept user input. +As you saw in Chapter 1, the `main` function is the entry point into the +program: ```rust,ignore fn main() { ``` -As you’ve 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. +The `fn` syntax declares a new function, the `()` indicate there are no +arguments, and `{` starts the body of the function. + +As you also learned in Chapter 1, `println!` is a macro that prints a string to +the screen: ```rust,ignore println!("Guess the number!"); @@ -128,142 +135,139 @@ println!("Guess the number!"); println!("Please input your guess."); ``` -As we learned in Chapter 1, `println!()` is a macro that prints a string to the -screen. This is just a prompt stating what the game is and requesting input from -the user. +This code is just printing a prompt stating what the game is and requesting +input from the user. -### Storing Values with Variable Bindings +### Storing Values with Variables -Next we need to store the user input. +Next, we'll create a place to store the user input, like this: ```rust,ignore let mut guess = String::new(); ``` -Now we’re getting interesting! There’s 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 *variable bindings*. Here's another example: +Now the program is getting interesting! There’s a lot going on in this little +line. Notice that this is a `let` statement, which is used to create +*variables*. Here’s another 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 Rust’s variable bindings have -a few differences. - -For example, they’re immutable by default. To make our binding mutable, our -example uses `mut` before the binding name. +This line will create a new variable named `foo` and bind it to the value +`bar`. In Rust, variables are immutable by default. The following example shows +how to use `mut` before the variable name to make a variable mutable: ```rust -let foo = 5; // immutable. +let foo = 5; // immutable let mut bar = 5; // mutable ``` -> Note: The `//` syntax will start a comment that continues until the end of the +> Note: The `//` syntax starts a comment that continues 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 the value it’s -bound to: `String::new()`. +Now you know that `let mut guess` will introduce a mutable variable named +`guess`. On the other side of the equal sign (`=`) is the value that `guess` is +bound to, which is the result of calling `String::new`, a function that returns +a new instance of a `String`. `String` is a string +type provided by the standard library that is a growable, UTF-8 encoded bit of +text. -`String` is a string type, provided by the standard library. A -`String` is a growable, UTF-8 encoded bit of text. +The `::` syntax in the `::new` line indicates that `new` is an *associated +function* of the `String` type. An associated function is implemented on a type, +in this case `String`, rather than on a particular instance of a `String`. Some +languages call this a *static method*. -The `::` syntax in the `::new()` line indicates that `new()` is an *associated -function* of a particular type. An associated function is a function that is -associated with a type, in this case `String`, rather than a particular -instance of a `String`. Some languages call this a *static method*. +This `new` function creates a new, empty `String`. You’ll find a `new` function +on many types, because it’s a common name for a function that makes a new value +of some kind. -This `new()` function creates a new, empty `String`. -You’ll find a `new()` function on many types, as it’s a common name for making -a new value of some kind. +To summarize, the `let mut guess = String::new();` line has created a mutable +variable that is currently bound to a new, empty instance of a `String`. Whew! -So to summarize, the `let mut guess = String::new();` line has created a -mutable binding that is currently bound to a new, empty instance of a `String`. -Whew! - -Let’s move forward: +Recall that we included the input/output functionality from the standard +library with `use std::io;` on the first line of the program. Now we'll call an +associated function, `stdin`, on `io`: ```rust,ignore io::stdin().read_line(&mut guess) .expect("Failed to read line"); ``` -Remember how we said `use std::io;` on the first line of the program? We’re now -calling an associated function on it. If we didn’t `use std::io`, we could -have written this line as `std::io::stdin()`. +If we didn’t have the `use std::io` line at the beginning of the program, we +could have written this function call as `std::io::stdin`. The `stdin` function +returns an instance of `std::io::Stdin`, which is a +type that represents a handle to the standard input for your terminal. -This particular function returns an instance of `std::io::Stdin`, -which is a type that represents a handle to the standard input for your -terminal. +The next part of the code, `.read_line(&mut guess)`, calls the +`read_line` method on the standard input handle to +get input from the user. We’re also passing one argument to `read_line`: `&mut +guess`. -The next part, `.read_line(&mut guess)`, calls the `readline()` method on the standard input handle to get input from the user. We’re also -passing one argument to `read_line()`: `&mut guess`. - -The job of `read_line()` is to take whatever the user types into standard input +The job of `read_line` is to take whatever the user types into standard input and place that into a string, so it takes that string as an argument. The -string argument needs to be mutable so that the method can change the string's +string argument needs to be mutable so the method can change the string’s content by adding the user input. The `&` indicates that this argument is a *reference*, which gives you a way to -allow multiple parts of your code to access to one piece of data without -needing to copy that data into memory multiple times. References are a complex -feature, and one of Rust’s major advantages is how safe and easy it is to use -references. We don’t need to know a lot of those details to finish our program -right now, though; Chapter XX will cover references 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` to make it -mutable. +let multiple parts of your code access one piece of data without needing to +copy that data into memory multiple times. References are a complex feature, +and one of Rust’s major advantages is how safe and easy it is to use +references. You don’t need to know a lot of those details to finish this +program: Chapter 4 will explain references more thoroughly. For now, all you +need to know is that like variables, references are immutable by default. +Hence, we need to write `&mut guess` rather than `&guess` to make it mutable. -We’re not quite done with this line of code. While it’s a single line of text, -it’s only the first part of the single logical line of code. The second part is -this method: +We’re not quite done with this line of code. Although it’s a single line of +text, it’s only the first part of the single logical line of code. The second +part is this method: ```rust,ignore .expect("Failed to read line"); ``` -When you call a method with the `.foo()` syntax, it's often wise to introduce a -newline and other whitespace. This helps you split up long lines. We _could_ -have written this code as: +When you call a method with the `.foo()` syntax, it’s often wise to introduce a +newline and other whitespace to help break 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 we’ve split it up, two lines for two method -calls. Now let's see what this line does. +However, one long line is difficult to read, so it’s best to divide it, two +lines for two method calls. Now let’s discuss what this line does. ### Handling Potential Failure with the `Result` Type -We mentioned that `read_line()` puts what the user types into the string we -pass it, but it also returns a value: in this case, an `io::Result`. Rust has a -number of types named `Result` in its standard library: a generic `Result`, and -then specific versions for sub-libraries, like `io::Result`. +As mentioned earlier, `read_line` puts what the user types into the string we're +passing it, but it also returns a value—in this case, an +`io::Result`. Rust has a number of types named +`Result` in its standard library: a generic `Result` as +well as specific versions for submodules, such as `io::Result`. -The `Result` types are enums, which is short for *enumeration*. An -enumeration is a type that can have a fixed set of values, which are called the -`enum`'s *variants*. We will be covering enums in more detail in Chapter XX. +The `Result` types are *enumerations*, often referred +to as *enums*. An enumeration is a type that can have a fixed set of values, +and those values are called the enum’s *variants*. Chapter 6 will cover enums +in more detail. -For `Result`, the variants are `Ok` or `Err`. `Ok` means the operation was +For `Result`, the variants are `Ok` or `Err`. `Ok` indicates the operation was successful, and inside the `Ok` variant is the successfully generated value. -`Err` means the operation failed, and the `Err` contains information about how -or why the operation failed. +`Err` means the operation failed, and `Err` contains information about how or +why the operation failed. 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 that we can call. If -this instance of `io::Result` is an `Err` value, `expect()` will cause our -program to crash and display the message that we passed as an argument to -`expect()`. In this case, if the `read_line()` method returns an `Err`, it would +Values of the `Result` type, like any type, have methods defined on them. An +instance of `io::Result` has an `expect` method that +you can call. If this instance of `io::Result` is an `Err` value, `expect` will +cause the program to crash and display the message that you passed as an +argument to `expect`. If the `read_line` method returns an `Err`, it would likely be the result of an error coming from the underlying operating system. -If this instance of `io::Result` is an `Ok` value, `expect()` will take the -return value that `Ok` is holding out of the `Ok` and return just that value to -us so that we can use it. In this case, that value will be what the user +If this instance of `io::Result` is an `Ok` value, `expect` will take the +return value that `Ok` is holding and return just that value to you so you +could use it. In this case, that value is the number of characters the user entered into standard input. -If we don't call `expect()`, our program will compile, but we’ll get a warning: +If we don’t call `expect`, the program will compile, but we’ll get a warning: ```bash $ cargo build @@ -274,26 +278,26 @@ src/main.rs:10 io::stdin().read_line(&mut guess); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` -Rust warns that we haven’t used the `Result` value, telling us that we -haven’t handled a possible error. The right way to suppress the warning is to -actually write error handling, but if we just want to crash the program when a -problem occurs, we can use `expect()`. We’ll save recovering from errors for a -future project. +Rust warns that we haven’t used the `Result` value returned from `read_line`, +indicating that the program hasn’t handled a possible error. The right way to +suppress the warning is to actually write error handling, but since we just +want to crash this program when a problem occurs, we can use `expect`. You’ll +learn about recovering from errors in Chapter 9. -### Printing Values with `println!()` Placeholders +### Printing Values with `println!` Placeholders -There’s only one line of this first example left, aside from the closing curly -brace: +Aside from the closing curly brace, there’s only one more line to discuss in +the code added so far, which is the following: ```rust,ignore - println!("You guessed: {}", guess); +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. You can print -more than one value this way: the first `{}` holds the first value listed 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: +This line prints out the string we saved the user’s input in. The set of `{}` +is a placeholder that holds a value in place. You can print more than one value +using `{}`: the first set of `{}` holds the first value listed after the format +string, the second set holds the second value, and so on. Printing out multiple +values in one call to `println!` would look like this: ```rust let x = 5; @@ -302,12 +306,11 @@ let y = 10; println!("x = {} and y = {}", x, y); ``` -Which would print out "x = 5 and y = 10". +This code would print out `x = 5 and y = 10`. ### Testing the First Part -Back to our guessing game, let's test what we have so far. We can run it with -`cargo run`: +Let’s test the first part of the guessing game. You can run it using `cargo run`: ```bash $ cargo run @@ -319,27 +322,29 @@ Please input your guess. You guessed: 6 ``` -All right! Our first part is done: we can get input from the keyboard and then -print it back out. +At this point, the first part of the game is done: we're getting input from the +keyboard and then printing it. ## Generating a Secret Number -Next, we need to generate a secret number that the user is trying to guess. The -secret number should be different every time so that the game is fun to play -more than once. So we'd like to have a random number between 1 and 100. Rust -does not yet include random number functionality in its standard library. The -Rust team does, however, provide a `rand` crate at *https://crates.io/crates/rand*. +Next, we need to generate a secret number that the user will try to guess. The +secret number should be different every time so the game is fun to play more +than once. Let’s use a random number between 1 and 100 so the game isn’t too +difficult. Rust doesn’t yet include random number functionality in its standard +library. However, the Rust team does provide a `rand` crate at *https://crates.io/crates/rand*. + ### Using a Crate to Get More Functionality -Remember that *crate* is what we call a package of Rust code. The project we’ve -been building is a *binary crate*, which is an executable. The `rand` crate is -a *library crate*, which contains code intended to be used in other programs. +Remember that a *crate* is a package of Rust code. The project we’ve been +building is a *binary crate*, which is an executable. The `rand` crate is a +*library crate*, which contains code intended to be used in other programs. -Cargo's use of external crates is where it really shines. Before we can write -the code using `rand`, we need to modify our `Cargo.toml` to include the `rand` -crate as a dependency. Open it up, and add this line at the bottom beneath the -`[dependencies]` section header that Cargo created for you: +Cargo’s use of external crates is where it really shines. Before we can write +code that uses `rand`, we need to modify the `Cargo.toml` file to include the +`rand` crate as a dependency. Open that file now and add the following line to +the bottom beneath the `[dependencies]` section header that Cargo created for +you: Filename: Cargo.toml @@ -350,16 +355,17 @@ rand = "0.3.14" ``` In the `Cargo.toml` file, everything that follows a header is part of a section -that goes until another section starts. Cargo uses the `[dependencies]` section -to know what external crates your project depends on and what versions of those -crates you require. In this case, we’ve specified the `rand` crate with the -semantic version specifier `0.3.14`. Cargo understands Semantic -Versioning (sometimes called *semver*), which is 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". +that continues until another section starts. The `[dependencies]` section is +where you tell Cargo which external crates your project depends on and which +versions of those crates you require. In this case, we’ll specify the `rand` +crate with the semantic version specifier `0.3.14`. Cargo understands Semantic +Versioning (sometimes called *SemVer*), which is a +standard for writing version numbers. The number `0.3.14` is actually shorthand +for `^0.3.14`, which means “any version that has a public API compatible with +version `0.3.14`.” -Now, without changing any of our code, let’s build our project: +Now, without changing any of the code, let’s build the project, as shown in +Listing 2-2: ```bash $ cargo build @@ -371,68 +377,76 @@ $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) ``` + +Listing 2-2: The output from running cargo build after adding the rand crate as +a dependency + + You may see different version numbers (but they will all be compatible with -your code, thanks to semver!) and the lines may be in a different order. +the code, 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 -*https://crates.io*. Crates.io is where people in the Rust ecosystem -post their open source Rust projects for others to use. +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 at *https://crates.io*. Crates.io is where people in the Rust ecosystem post +their open source Rust projects for others to use. -After updating the registry, Cargo checks our `[dependencies]` and downloads -any we don’t have yet. In this case, while we only listed `rand` as a -dependency, we’ve also grabbed a copy of `libc`, because `rand` depends on + +After updating the registry, Cargo checks the `[dependencies]` section and +downloads any you don’t have yet. In this case, although we only listed `rand` +as a dependency, Cargo also grabbed a copy of `libc`, because `rand` depends on `libc` to work. After downloading them, Rust compiles them and then compiles -our project. +the project with the dependencies available. -If we run `cargo build` again, we’ll get different output: - -```bash -$ cargo build -``` - -That’s 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. There’s -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, -we’ll only see one line: +If you immediately run `cargo build` again without making any changes, you won’t +get any output. Cargo knows it has already downloaded and compiled the +dependencies, and you haven't changed anything about them in your `Cargo.toml` +file. Cargo also knows that you haven't changed anything about your code, so it +doesn't recompile that either. With nothing to do, it simply exits. If you open +up the `src/main.rs` file, make a trivial change, then save it and build again, +you’ll only see one line of output: ```bash $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) ``` -This just updates the build with your tiny change to the `main.rs` file. -#### The Cargo.lock File that Ensures Reproducible Builds +This line shows Cargo only updates the build with your tiny change to the +`src/main.rs` file. Your dependencies haven't changed, so Cargo knows it can +reuse what it has already downloaded and compiled for those. It just rebuilds +your part of the code. -Cargo has a mechanism to make sure we can rebuild the exact same artifact every -time we or anyone else builds our code: Cargo will only use the versions of the -dependencies you specified until you say otherwise. For example, what happens -if next week version `v0.3.15` of the `rand` crate comes out, containing an -important bugfix, but that also contains a regression that will break our code? +#### The Cargo.lock File Ensures Reproducible Builds -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 of your -dependencies 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 use the versions specified there rather than doing all the work -of figuring out versions again. This lets you have a reproducible build -automatically. In other words, our project will stay at `0.3.14` until we -explicitly upgrade, thanks to the lock file. +Cargo has a mechanism that ensures you can rebuild the same artifact every time +you or anyone else builds your code: Cargo will use only the versions of the +dependencies you specified until you indicate otherwise. For example, what +happens if next week version `v0.3.15` of the `rand` crate comes out and +contains an important bug fix but also contains a regression that will break +your code? + +The answer to this problem is the `Cargo.lock` file, which was created the +first time you ran `cargo build` and is now in your `guessing_game` directory. +When you build a project for the first time, Cargo figures out all the +versions of the dependencies that fit the criteria and 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 use the versions specified there +rather than doing all the work of figuring out versions again. This lets you +have a reproducible build automatically. In other words, your project will +remain at `0.3.14` until you explicitly upgrade, thanks to the `Cargo.lock` +file. #### Updating a Crate to Get a New Version -When we _do_ want to update a crate, Cargo has another command, -`update`, which will: +When you _do_ want to update a crate, Cargo provides another command, `update`, +which will: -- Ignore the `Cargo.lock` file and figure out all the latest versions that fit - our specifications in `Cargo.toml`. -- If that works, write those versions out to the lock file. +1. Ignore the `Cargo.lock` file and figure out all the latest versions that fit + your specifications in `Cargo.toml`. +1. If that works, Cargo will write those versions to the `Cargo.lock` file. But by default, Cargo will only look for versions larger than `0.3.0` and smaller than `0.4.0`. If the `rand` crate has released two new versions, -`0.3.15` and `0.4.0`, this is what we would see if we ran `cargo update`: +`0.3.15` and `0.4.0`, you would see the following if you ran `cargo update`: ```bash $ cargo update @@ -443,9 +457,8 @@ $ cargo update At this point, you would also notice a change in your `Cargo.lock` file noting that the version of the `rand` crate you are now using is `0.3.15`. -If we wanted to use `rand` version `0.4.0` or any version in the `0.4.x` -series, we’d have to update what is in the `Cargo.toml` file to look like this -instead: +If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x` +series, you’d have to update the `Cargo.toml` file to look like this instead: ```toml [dependencies] @@ -453,19 +466,20 @@ instead: rand = "0.4.0" ``` -The next time we `cargo build`, assuming that the `rand` crate version `0.4.0` -has been released, Cargo will update the crates index and re-evaluate our -`rand` requirements according to the new version we have specified. +The next time you run `cargo build`, Cargo will update the registry of crates +available and reevaluate your `rand` requirements according to the new version +you specified. -There’s a lot more to say about Cargo and its ecosystem that we will get into -in Chapter XX, but for now, that’s 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. +There’s a lot more to say about Cargo and its +ecosystem that Chapter XX will discuss, but for +now, that’s all you need to know. Cargo makes it very easy to reuse libraries, +so Rustaceans are able to write smaller projects that are assembled from a +number of packages. ### Generating a Random Number -Let’s get on to actually _using_ `rand`. Our next step is to update our -`main.rs` code as follows: +Let’s start _using_ `rand`. The next step is to update `src/main.rs`, as shown +in Listing 2-3: Filename: src/main.rs @@ -493,48 +507,41 @@ fn main() { } ``` -First we've added a line to the top, `extern crate rand`, that lets Rust know -we’ll be making use of that external dependency. This also does the equivalent -of calling `use rand`, so we can now call anything in the `rand` crate by -prefixing it with `rand::`. +Listing 2-3: Code changes needed in order to generate a random number -Next, we added another `use` line: `use rand::Rng`. `Rng` is a trait that +We're adding a `extern crate rand;` line to the top that lets Rust know we’ll be +using that external dependency. This also does the equivalent of calling `use +rand`, so now we can call anything in the `rand` crate by prefixing it with +`rand::`. + +Next, we're adding another `use` line: `use rand::Rng`. `Rng` is a trait that defines methods that random number generators implement, and this trait must be -in scope for us to use those methods. We'll cover traits in detail in the -Traits section in Chapter XX. +in scope for us to use those methods. Chapter 10 will cover traits in detail. -We also added two more lines in the middle: +Also, we're adding two more lines in the middle. The `rand::thread_rng` function +will give us the particular random number generator that we’re going to use: +one that is local to the current thread of execution and seeded by the +operating system. Next, we call the `gen_range` method on the random number +generator. This method is defined by the `Rng` trait that we brought into +scope with the `use rand::Rng` statement. The `gen_range` method takes two +numbers as arguments and generates a random number between them. It’s inclusive +on the lower bound but exclusive on the upper bound, so we need to specify `1` +and `101` to request a number between 1 and 100. -```rust,ignore -let secret_number = rand::thread_rng().gen_range(1, 101); - -println!("The secret number is: {}", secret_number); -``` - -`rand::thread_rng()` is a function that will give us the particular random -number generator that we're going to use: one that is local to our current -thread of execution and seeded by the operating system. Next, we call the -`gen_range()` method on our random number generator. This method is one that is -defined by the Rng trait that we brought into scope with the `use rand::Rng` -statement above. `gen_range()` takes two numbers as arguments and generates a -random number between them. It’s 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. - -Knowing what traits to import and what functions and methods to use from a -crate isn't something that you'll just *know*. Instructions for using a crate -are in each crate's documentation. Another neat feature of Cargo is that you -can run the `cargo doc --open` command to build documentation provided by all -of your dependencies locally and then open it in your browser. If you're +Knowing which traits to import and which functions and methods to use from a +crate isn’t something that you’ll just *know*. Instructions for using a crate +are in each crate’s documentation. Another neat feature of Cargo is that you +can run the `cargo doc --open` command that will build documentation provided +by all of your dependencies locally and open it in your browser. If you’re interested in other functionality in the `rand` crate, for example, run `cargo -doc --open` then click on "rand" in the sidebar on the left. +doc --open` and click `rand` in the sidebar on the left. -The second line that we added to our code prints out the secret number. This is -useful while we’re developing our program to let us easily test it out, but -we’ll be deleting it for the final version. It’s not much of a game if it -prints out the answer when you start it up! +The second line that we added to the code prints the secret number. This is +useful while we’re developing the program to be able to test it, but we’ll +delete it from the final version. It’s not much of a game if the program prints +the answer as soon as it starts! -Try running our new program a few times: +Try running the program a few times: ```bash $ cargo run @@ -557,10 +564,10 @@ You guessed: 5 You should get different random numbers, and they should all be numbers between 1 and 100. Great job! -## Comparing Our Guesses +## Comparing the Guess to the Secret Number -Now that we’ve got user input, let’s compare our guess to the secret number. -Here’s that part of our next step: +Now that we have user input and a random number, we can compare them. That +step is shown in Listing 2-4: Filename: src/main.rs @@ -595,11 +602,13 @@ fn main() { } ``` -There are a few new bits here. The first is another `use`, bringing a type -called `std::cmp::Ordering` into scope from the standard crate. `Ordering` is +Listing 2-4: Handling the possible return values of comparing two numbers + +The first new bit here is another `use`, bringing a type called +`std::cmp::Ordering` into scope from the standard library. `Ordering` is another enum, like `Result`, but the variants for `Ordering` are `Less`, `Greater`, and `Equal`. These are the three outcomes that are possible when you -compare two things. +compare two values. Then we add five new lines at the bottom that use the `Ordering` type: @@ -611,61 +620,66 @@ match guess.cmp(&secret_number) { } ``` -The `cmp()` method compares two values, and can be called on anything that can -be compared. It takes a reference to the thing you want to compare it to, so -here it's comparing our `guess` to our `secret_number`. `cmp()` returns a -variant of the `Ordering` type we imported with the `use` statement earlier. We -use a `match` statement to decide what to do next based on which -variant of `Ordering` we got back from our call to `cmp()` with the values in -`guess` and `secret_number`. +The `cmp` method compares two values and can be called on anything that can be +compared. It takes a reference to whatever you want to compare with: here it’s +comparing the `guess` to the `secret_number`. `cmp` returns a variant of the +`Ordering` enum we imported with the `use` statement. We use a +`match` expression to decide what to do next based on +which variant of `Ordering` was returned from the call to `cmp` with the values +in `guess` and `secret_number`. -`match` statements are made up of *arms*. An arm consists of a *pattern* and -the code that we should run if the value given to the beginning of the `match` -statement fits that arm's pattern. Rust takes the value given to `match` and -looks through each arm's pattern in turn. The `match` construct and patterns -are powerful features in Rust that will be covered in detail in Chapter XX and -Chapter XX, respectively. +A `match` expression is made up of *arms*. An arm consists of a *pattern* and +the code that should be run if the value given to the beginning of the `match` +expression fits that arm’s pattern. Rust takes the value given to `match` and +looks through each arm’s pattern in turn. The `match` construct and patterns +are powerful features in Rust that let you express a variety of situations your +code might encounter and helps ensure that you handle them all. These features +will be covered in detail in Chapter 6 and Chapter XX, respectively. -Let's walk through an example of what would happen with our `match`. Say that -the user has guessed 50, and the randomly-generated secret number this time -is 38. So when we compare 50 to 38, the `cmp()` method will return -`Ordering::Greater`, since 50 is greater than 38. `Ordering::Greater` is the -value that the `match` statement gets. It looks at the first arm's pattern, -`Ordering::Less`, and says nope, the value we have (`Ordering::Greater`) does -not match `Ordering::Less`. So it ignores the code in that arm and moves on to -the next arm. The next arm's pattern, `Ordering::Greater`, **does** match -`Ordering::Greater`! The associated code in that arm will get executed, which -prints "Too big!" to the screen. Then we're done with the `match` statement; we -don't look at the last arm at all in this particular scenario. +Let’s walk through an example of what would happen with the `match` expression +used here. Say that the user has guessed 50, and the randomly generated secret +number this time is 38. When the code compares 50 to 38, the `cmp` method will +return `Ordering::Greater`, because 50 is greater than 38. `Ordering::Greater` +is the value that the `match` expression gets. It looks at the first arm’s +pattern, `Ordering::Less`, but the value `Ordering::Greater` does not match +`Ordering::Les`s. So it ignores the code in that arm and moves to the next arm. +The next arm’s pattern, `Ordering::Greater`, **does** match +`Ordering::Greater`! The associated code in that arm will execute and print +`Too big!` to the screen. The `match` expression ends because it has no need to +look at the last arm in this particular scenario. -However, this code won’t quite compile yet. Let’s try it: +However, the code in Listing 2-4 won’t compile yet. Let’s try it: ```bash $ cargo build Compiling guessing_game v0.1.0 (file:///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[E0308]: mismatched types + --> src/main.rs:23:21 + | +23 | match guess.cmp(&secret_number) { + | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable + | + = note: expected type `&std::string::String` + = note: found type `&{integer}` + error: aborting due to previous error Could not compile `guessing_game`. ``` -Whew! This is a big error. The core of the error says 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` and didn’t make us write the type out. Our -`secret_number` on the other hand is a number type. There are a few number -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; `i64`, a sixty-four-bit -number; or others. Rust defaults to an `i32`, so that's the type of -`secret_number`. The error is because Rust will not compare two different types. +The core of the error states that there are *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` and didn’t make us write the type. The `secret_number`, on the other +hand, is a number type. A few number types can have a value between 1 and 100: +`i32`, a 32-bit number; `u32`, an unsigned 32-bit number; `i64`, a 64-bit +number; as well as others. Rust defaults to an `i32`, which is the type of +`secret_number` unless we add type information elsewhere that would cause Rust +to infer a different numerical type. The reason for the error is that Rust will +not compare a string and a number 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; add this to your program: +Ultimately, we want to convert the `String` the program reads as input into a +real number type so we can compare it to the guess numerically. We can do +that by adding the following two lines to the `main` function body: Filename: src/main.rs @@ -710,44 +724,47 @@ let guess: u32 = guess.trim().parse() .expect("Please type a number!"); ``` -We create a variable binding `guess`. But wait a minute, don't we already have -a variable binding named `guess`? We do, but Rust allows us to *shadow* the -previous value of `guess` with a new one. This is often used in this exact -situation, where we want to convert a value from one type into another type. -Shadowing lets us re-use the `guess` variable name rather than forcing us to -come up with two unique bindings, like `guess_str` and `guess` or something. +We create a variable named `guess`. But wait, doesn’t the program +already have a variable named `guess`? It does, but Rust allows us to +*shadow* the previous value of `guess` with a new one. This feature is often +used in similar situations in which you want to convert a value from one type +to another type. Shadowing lets us reuse the `guess` variable name rather than +forcing us to create two unique variables, like `guess_str` and `guess` for +example. (Chapter 3 covers shadowing in more detail.) -We bind `guess` to the expression `guess.trim().parse()`. +We bind `guess` to the expression `guess.trim().parse()`. The `guess` in the +expression refers to the original `guess` that was a `String` with the input in +it. The `trim` method on a `String` instance will eliminate any whitespace at +the beginning and end. `u32` can only contain numerical characters, but the +user must press the Return key to satisfy `read_line`. When the user presses +Return, a newline character is added to the string. For example, if the user +types 5 and presses return, `guess` looks like this: `5\n`. The `\n` represents +“newline,” the return key. The `trim` method eliminates `\n`, resulting in just +`5`. -The `guess` in the expression refers to the original `guess` 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. Our u32 can only contain numerical -characters, but we have to press the "return" key to satisfy `read_line()`. -When we press the return key, it introduces a newline character. For example, -if we type `5` and hit return, `guess` looks like this: `5\n`. The `\n` -represents "newline", the return key. The `trim()` method gets rid of this, -leaving our string with only the `5`. +The `parse` method on strings parses a string into some +kind of number. Because this method can parse a variety of number types, we +need to tell Rust the exact number type we want by using `let guess: u32`. The +colon (`:`) after `guess` tells Rust we’ll annotate the variable’s type. Rust +has a few built-in number types; the `u32` seen here is an unsigned, 32-bit +integer. It’s a good default choice for a small positive number. You’ll learn +about other number types in Chapter 3. Additionally, the `u32` annotation in +this example program and the comparison with `secret_number` means that Rust +will infer that `secret_number` should be a `u32` as well. So now the +comparison will be between two values of the same type! -The `parse()` method on strings parses a string into some kind of -number. Since this method can parse a variety of number types, we need to tell -Rust the exact type of number we want with `let guess: u32`. The colon (`:`) -after `guess` tells Rust we’re going to annotate its type. Rust has a few -built-in number types, but we’ve chosen `u32`, an unsigned, thirty-two bit -integer. It’s a good default choice for a small positive number. You'll see the -other number types in Chapter XX. +The call to `parse` could easily cause an error. If, for example, the string +contained `A👍%`, there would be no way to convert that to a number. Because it +might fail, the `parse` method returns a `Result` type, much like the +`read_line` method does as discussed earlier in “Handling Potential Failure +with the Result Type” on page XX. We’ll treat this `Result` the same way by +using the `expect` method again. If `parse` returns an `Err` `Result` variant +because it couldn’t create a number from the string, the `expect` call will +crash the game and print the message we give it. If `parse` can successfully +convert the string to a number, it will return the `Ok` variant of `Result`, +and `expect` will return the number that we want from the `Ok` value. -Our call to `parse()` could quite easily cause an error, if, for example, our -string contained `A👍%`; there’d be no way to convert that to a number. Because -it might fail, the `parse()` method returns a `Result` type, much like the -`read_line()` method does that we discussed earlier. We're going to treat this -`Result` the same way by using the `expect()` method again. If `parse()` -returns an `Err` `Result` variant because it could not create a number from the -string, the `expect()` call will crash the game and print the message we give -it. If `parse()` can successfully turn the string into a number, it will return -the `Ok` variant of `Result`, and `expect()` will return the number that we -want that it will take out of the `Ok` value for us. - -Let’s try our program out! +Let’s run the program now! ```bash $ cargo run @@ -761,17 +778,17 @@ 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 to verify the different -behavior with different kinds of input: guess the number correctly, guess a -number that is too high, and guess a number that is too low. +Nice! Even though spaces were added before the guess, the program still figured +out that the user guessed 76. Run the program a few times to verify the +different behavior with different kinds of input: guess the number correctly, +guess a number that is too high, and guess a number that is too low. -Now we’ve got most of the game working, but we can only make one guess. Let’s -change that by adding a loop! +We have most of the game working now, but the user can make only one guess. +Let’s change that by adding a loop! ## Allowing Multiple Guesses with Looping -The `loop` keyword gives us an infinite loop. We'll add that in to give us more +The `loop` keyword gives us an infinite loop. Add that now to give users more chances at guessing the number: Filename: src/main.rs @@ -812,16 +829,17 @@ fn main() { } ``` -As you can see, we've moved everything from the guess input onwards into a -loop. Make sure to indent those lines another four spaces each, and try it out. -You'll notice we have a new problem because the program is doing exactly what we -told it to do: ask for another guess forever! It doesn't seem like we can quit! +As you can see, we've moved everything into a loop from the guess input prompt +onward. Be sure to indent those lines another four spaces each, and run the +program again. Notice that there is a new problem because the program is doing +exactly what we told it to do: ask for another guess forever! It doesn’t seem +like the user can quit! -We could always halt the program by using the keyboard shortcut `control-c`. -There's another way to escape the monster we've created that will infinitely -demand more guesses, though, that can be found in our discussion about -`parse()`: if we give a non-number answer, the program will crash. We can use -that to quit! Observe: +The user could always halt the program by using the keyboard shortcut `Ctrl-C`. +But there’s another way to escape this insatiable monster that we mentioned in +the `parse` discussion in “Comparing the Guesses” on page XX: if the user +enters a non-number answer, the program will crash. The user can take advantage +of that in order to quit, as shown here: ```bash $ cargo run @@ -848,13 +866,13 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace. error: Process didn't exit successfully: `target/debug/guess` (exit code: 101) ``` -This method means that typing `quit` actually quits the game, but so does any -other non-number input. This is suboptimal, to say the least. We want the game -to automatically stop when the correct number is guessed. +Typing `quit` actually quits the game, but so will any other non-number input. +However, this is suboptimal to say the least. We want the game to automatically +stop when the correct number is guessed. -#### Quitting When you Win +#### Quitting After a Correct Guess -Let’s program the game to quit when you win by adding a `break` in that case: +Let’s program the game to quit when the user wins by adding a `break`: Filename: src/main.rs @@ -897,16 +915,16 @@ fn main() { } ``` -By adding the `break` line after `You win!`, we’ll exit the loop when we guses -the secret number correctly. Exiting the loop also means exiting the program, -since the loop is the last thing in `main()`. +By adding the `break` line after `You win!`, the program will exit the loop +when the user guesses the secret number correctly. Exiting the loop also means +exiting the program, because the loop is the last part of `main`. #### Handling Invalid Input -For our final refinement of the game's behavior, rather than crashing the -program when someone inputs a non-number, we want the game to ignore it so we -can continue guessing. We can do that by altering the line where we convert -`guess` from a `String` to a `u32`: +To further refine the game’s behavior, rather than crashing the program when +the user inputs a non-number, let’s make the game ignore a non-number so the +user can continue guessing. We can do that by altering the line where `guess` is +converted from a `String` to a `u32`: ```rust,ignore let guess: u32 = match guess.trim().parse() { @@ -915,28 +933,30 @@ let guess: u32 = match guess.trim().parse() { }; ``` -This is how you generally move from "crash on error" to "actually handle the -error": by switching from an `expect()` statement to a `match` statement. -Remember that `parse()` returns a `Result` type, and `Result` is an enum that -has the variants `Ok` or `Err`. We're going to use a `match` statement here, -like we did with the `Ordering` result of the `cmp()` method. +Switching from an `expect` call to a `match` expression is how you generally +move from crash on error to actually handling the error. Remember that `parse` +returns a `Result` type, and `Result` is an enum that has the variants `Ok` or +`Err`. We're using a `match` expression here, like we did with the `Ordering` +result of the `cmp` method. -If `parse()` is able to successfully turn the string into a number, it will -return an `Ok` value that contains the resulting number. That `Ok` value will -match the first arm's pattern, and the match statement will just return the -`num` value that `parse()` produced and put inside the `Ok` value. That number -will end up right where we want it, in the new `guess` binding we're creating. +If `parse` is able to successfully turn the string into a number, it will return +an `Ok` value that contains the resulting number. That `Ok` value will match the +first arm’s pattern, and the `match` expression will just return the `num` value +that `parse` produced and put inside the `Ok` value. That number will end up +right where we want it in the new `guess` variable we’re creating. -If `parse()` is *not* able to turn the string into a number, it will return an +If `parse` is not able to turn the string into a number, it will return an `Err` value that contains more information about the error. The `Err` value does not match the `Ok(num)` pattern in the first match arm, but it does match -the `Err(_)` pattern in the second arm. The `_` is a catch-all value; we're -saying we want to match all `Err` values, no matter what information they have -inside them. So we execute the second arm's code, `continue`: this means to go -to the next iteration of the `loop` and ask for another guess. So we have -effectively ignored all errors that `parse()` might hit! +the `Err(_)` pattern in the second arm. The `_` is a catchall value; in this +example, we’re saying we want to match all `Err` values, no matter what +information they have inside them. So the program will execute the second arm’s +code, `continue`, which means to go to the next iteration of the loop and ask +for another guess. So effectively, the program ignores all errors that `parse` +might encounter! -Now everything in our program should work as we expect it to! Let’s try it: +Now everything in the program should work as expected. Let’s try it by running +`cargo run`: ```bash $ cargo run @@ -960,10 +980,10 @@ You guessed: 61 You win! ``` -Awesome! With one tiny last tweak, we can finish the guessing game: we're still -printing out the secret number. That was good for testing, but it kind of ruins -the game. Let's delete the `println!` that outputs the secret number. Here’s our -full, final code: +Awesome! With one tiny final tweak, we will finish the guessing game: recall +that the program is still printing out the secret number. That worked well for +testing, but it ruins the game. Let’s delete the `println!` that outputs the +secret number. Listing 2-5 shows the final code: Filename: src/main.rs @@ -1006,15 +1026,17 @@ fn main() { } ``` -## Complete! +Listing 2-5: Complete code of the guessing game -At this point, you have successfully built the Guessing Game! Congratulations! +## Summary -This project was a hands-on way to introduce you to a lot of new Rust concepts: +At this point, you’ve successfully built the guessing game! Congratulations! + +This project was a hands-on way to introduce you to many new Rust concepts: `let`, `match`, methods, associated functions, using external crates, and more. -In the next few chapters, we will go through these concepts in more detail. -Chapter 3 covers concepts that most programming languages have, like variables, -data types, and functions, and shows how to use them in Rust. Chapter 4 gets -into ownership, which is the feature of Rust that is most different from other -languages. Chapter 5 discusses structs and method syntax, and Chapter 6 -endeavors to explain enums. +In the next few chapters, you’ll learn about these concepts in more detail. +Chapter 3 covers concepts that most programming languages have, such as +variables, data types, and functions, and shows how to use them in Rust. +Chapter 4 explores ownership, which is a Rust feature that is most different +from other languages. Chapter 5 discusses structs and method syntax, and +Chapter 6 endeavors to explain enums.