]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | # Programming a Guessing Game |
2 | ||
3 | Let’s jump into Rust by working through a hands-on project together! This | |
4 | chapter introduces you to a few common Rust concepts by showing you how to use | |
5 | them in a real program. You’ll learn about `let`, `match`, methods, associated | |
6 | functions, using external crates, and more! The following chapters will explore | |
7 | these ideas in more detail. In this chapter, you’ll practice the fundamentals. | |
8 | ||
9 | We’ll implement a classic beginner programming problem: a guessing game. Here’s | |
10 | how it works: the program will generate a random integer between 1 and 100. It | |
11 | will then prompt the player to enter a guess. After a guess is entered, the | |
12 | program will indicate whether the guess is too low or too high. If the guess is | |
13 | correct, the game will print a congratulatory message and exit. | |
14 | ||
15 | ## Setting Up a New Project | |
16 | ||
17 | To set up a new project, go to the *projects* directory that you created in | |
18 | Chapter 1 and make a new project using Cargo, like so: | |
19 | ||
20 | ```text | |
21 | $ cargo new guessing_game | |
22 | $ cd guessing_game | |
23 | ``` | |
24 | ||
25 | The first command, `cargo new`, takes the name of the project (`guessing_game`) | |
26 | as the first argument. The second command changes to the new project’s | |
27 | directory. | |
28 | ||
29 | Look at the generated *Cargo.toml* file: | |
30 | ||
31 | <span class="filename">Filename: Cargo.toml</span> | |
32 | ||
33 | ```toml | |
34 | [package] | |
35 | name = "guessing_game" | |
36 | version = "0.1.0" | |
37 | authors = ["Your Name <you@example.com>"] | |
69743fb6 | 38 | edition = "2018" |
13cf67c4 XL |
39 | |
40 | [dependencies] | |
41 | ``` | |
42 | ||
43 | If the author information that Cargo obtained from your environment is not | |
44 | correct, fix that in the file and save it again. | |
45 | ||
46 | As you saw in Chapter 1, `cargo new` generates a “Hello, world!” program for | |
47 | you. Check out the *src/main.rs* file: | |
48 | ||
49 | <span class="filename">Filename: src/main.rs</span> | |
50 | ||
51 | ```rust | |
52 | fn main() { | |
53 | println!("Hello, world!"); | |
54 | } | |
55 | ``` | |
56 | ||
57 | Now let’s compile this “Hello, world!” program and run it in the same step | |
58 | using the `cargo run` command: | |
59 | ||
60 | ```text | |
61 | $ cargo run | |
62 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
63 | Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs | |
64 | Running `target/debug/guessing_game` | |
65 | Hello, world! | |
66 | ``` | |
67 | ||
68 | The `run` command comes in handy when you need to rapidly iterate on a project, | |
69 | as we’ll do in this game, quickly testing each iteration before moving on to | |
70 | the next one. | |
71 | ||
72 | Reopen the *src/main.rs* file. You’ll be writing all the code in this file. | |
73 | ||
74 | ## Processing a Guess | |
75 | ||
76 | The first part of the guessing game program will ask for user input, process | |
77 | that input, and check that the input is in the expected form. To start, we’ll | |
78 | allow the player to input a guess. Enter the code in Listing 2-1 into | |
79 | *src/main.rs*. | |
80 | ||
81 | <span class="filename">Filename: src/main.rs</span> | |
82 | ||
83 | ```rust,ignore | |
84 | use std::io; | |
85 | ||
86 | fn main() { | |
87 | println!("Guess the number!"); | |
88 | ||
89 | println!("Please input your guess."); | |
90 | ||
91 | let mut guess = String::new(); | |
92 | ||
93 | io::stdin().read_line(&mut guess) | |
94 | .expect("Failed to read line"); | |
95 | ||
96 | println!("You guessed: {}", guess); | |
97 | } | |
98 | ``` | |
99 | ||
100 | <span class="caption">Listing 2-1: Code that gets a guess from the user and | |
101 | prints it</span> | |
102 | ||
103 | This code contains a lot of information, so let’s go over it line by line. To | |
104 | obtain user input and then print the result as output, we need to bring the | |
105 | `io` (input/output) library into scope. The `io` library comes from the | |
106 | standard library (which is known as `std`): | |
107 | ||
108 | ```rust,ignore | |
109 | use std::io; | |
110 | ``` | |
111 | ||
112 | By default, Rust brings only a few types into the scope of every program in | |
113 | [the *prelude*][prelude]<!-- ignore -->. If a type you want to use isn’t in the | |
114 | prelude, you have to bring that type into scope explicitly with a `use` | |
115 | statement. Using the `std::io` library provides you with a number of useful | |
116 | features, including the ability to accept user input. | |
117 | ||
118 | [prelude]: ../std/prelude/index.html | |
119 | ||
120 | As you saw in Chapter 1, the `main` function is the entry point into the | |
121 | program: | |
122 | ||
123 | ```rust,ignore | |
124 | fn main() { | |
125 | ``` | |
126 | ||
127 | The `fn` syntax declares a new function, the parentheses, `()`, indicate there | |
128 | are no parameters, and the curly bracket, `{`, starts the body of the function. | |
129 | ||
130 | As you also learned in Chapter 1, `println!` is a macro that prints a string to | |
131 | the screen: | |
132 | ||
133 | ```rust,ignore | |
134 | println!("Guess the number!"); | |
135 | ||
136 | println!("Please input your guess."); | |
137 | ``` | |
138 | ||
139 | This code is printing a prompt stating what the game is and requesting input | |
140 | from the user. | |
141 | ||
142 | ### Storing Values with Variables | |
143 | ||
144 | Next, we’ll create a place to store the user input, like this: | |
145 | ||
146 | ```rust,ignore | |
147 | let mut guess = String::new(); | |
148 | ``` | |
149 | ||
150 | Now the program is getting interesting! There’s a lot going on in this little | |
151 | line. Notice that this is a `let` statement, which is used to create a | |
152 | *variable*. Here’s another example: | |
153 | ||
154 | ```rust,ignore | |
155 | let foo = bar; | |
156 | ``` | |
157 | ||
158 | This line creates a new variable named `foo` and binds it to the value of the | |
159 | `bar` variable. In Rust, variables are immutable by default. We’ll be | |
9fa01778 XL |
160 | discussing this concept in detail in the [“Variables and Mutability”] |
161 | [variables-and-mutability]<!-- ignore --> section in Chapter 3. The following | |
162 | example shows how to use `mut` before the variable name to make a variable | |
163 | mutable: | |
13cf67c4 XL |
164 | |
165 | ```rust,ignore | |
166 | let foo = 5; // immutable | |
167 | let mut bar = 5; // mutable | |
168 | ``` | |
169 | ||
170 | > Note: The `//` syntax starts a comment that continues until the end of the | |
171 | > line. Rust ignores everything in comments, which are discussed in more detail | |
172 | > in Chapter 3. | |
173 | ||
69743fb6 XL |
174 | Let's return to the guessing game program. You now know that `let mut guess` |
175 | will introduce a mutable variable named `guess`. On the other side of the equal | |
176 | sign (`=`) is the value that `guess` is bound to, which is the result of | |
177 | calling `String::new`, a function that returns a new instance of a `String`. | |
178 | [`String`][string]<!-- ignore --> is a string type provided by the standard | |
179 | library that is a growable, UTF-8 encoded bit of text. | |
13cf67c4 XL |
180 | |
181 | [string]: ../std/string/struct.String.html | |
182 | ||
183 | The `::` syntax in the `::new` line indicates that `new` is an *associated | |
184 | function* of the `String` type. An associated function is implemented on a type, | |
185 | in this case `String`, rather than on a particular instance of a `String`. Some | |
186 | languages call this a *static method*. | |
187 | ||
188 | This `new` function creates a new, empty string. You’ll find a `new` function | |
189 | on many types, because it’s a common name for a function that makes a new value | |
190 | of some kind. | |
191 | ||
192 | To summarize, the `let mut guess = String::new();` line has created a mutable | |
193 | variable that is currently bound to a new, empty instance of a `String`. Whew! | |
194 | ||
195 | Recall that we included the input/output functionality from the standard | |
196 | library with `use std::io;` on the first line of the program. Now we’ll call an | |
197 | associated function, `stdin`, on `io`: | |
198 | ||
199 | ```rust,ignore | |
200 | io::stdin().read_line(&mut guess) | |
201 | .expect("Failed to read line"); | |
202 | ``` | |
203 | ||
204 | If we hadn’t listed the `use std::io` line at the beginning of the program, we | |
205 | could have written this function call as `std::io::stdin`. The `stdin` function | |
206 | returns an instance of [`std::io::Stdin`][iostdin]<!-- ignore -->, which is a | |
207 | type that represents a handle to the standard input for your terminal. | |
208 | ||
209 | [iostdin]: ../std/io/struct.Stdin.html | |
210 | ||
211 | The next part of the code, `.read_line(&mut guess)`, calls the | |
212 | [`read_line`][read_line]<!-- ignore --> method on the standard input handle to | |
213 | get input from the user. We’re also passing one argument to `read_line`: `&mut | |
214 | guess`. | |
215 | ||
216 | [read_line]: ../std/io/struct.Stdin.html#method.read_line | |
217 | ||
218 | The job of `read_line` is to take whatever the user types into standard input | |
219 | and place that into a string, so it takes that string as an argument. The | |
220 | string argument needs to be mutable so the method can change the string’s | |
221 | content by adding the user input. | |
222 | ||
223 | The `&` indicates that this argument is a *reference*, which gives you a way to | |
224 | let multiple parts of your code access one piece of data without needing to | |
225 | copy that data into memory multiple times. References are a complex feature, | |
226 | and one of Rust’s major advantages is how safe and easy it is to use | |
227 | references. You don’t need to know a lot of those details to finish this | |
228 | program. For now, all you need to know is that like variables, references are | |
229 | immutable by default. Hence, you need to write `&mut guess` rather than | |
230 | `&guess` to make it mutable. (Chapter 4 will explain references more | |
231 | thoroughly.) | |
232 | ||
233 | ### Handling Potential Failure with the `Result` Type | |
234 | ||
235 | We’re not quite done with this line of code. Although what we’ve discussed so | |
236 | far is a single line of text, it’s only the first part of the single logical | |
237 | line of code. The second part is this method: | |
238 | ||
239 | ```rust,ignore | |
240 | .expect("Failed to read line"); | |
241 | ``` | |
242 | ||
243 | When you call a method with the `.foo()` syntax, it’s often wise to introduce a | |
244 | newline and other whitespace to help break up long lines. We could have | |
245 | written this code as: | |
246 | ||
247 | ```rust,ignore | |
248 | io::stdin().read_line(&mut guess).expect("Failed to read line"); | |
249 | ``` | |
250 | ||
251 | However, one long line is difficult to read, so it’s best to divide it: two | |
252 | lines for two method calls. Now let’s discuss what this line does. | |
253 | ||
254 | As mentioned earlier, `read_line` puts what the user types into the string | |
255 | we’re passing it, but it also returns a value—in this case, an | |
256 | [`io::Result`][ioresult]<!-- ignore -->. Rust has a number of types named | |
257 | `Result` in its standard library: a generic [`Result`][result]<!-- ignore --> | |
258 | as well as specific versions for submodules, such as `io::Result`. | |
259 | ||
260 | [ioresult]: ../std/io/type.Result.html | |
261 | [result]: ../std/result/enum.Result.html | |
262 | ||
263 | The `Result` types are [*enumerations*][enums]<!-- ignore -->, often referred | |
264 | to as *enums*. An enumeration is a type that can have a fixed set of values, | |
265 | and those values are called the enum’s *variants*. Chapter 6 will cover enums | |
266 | in more detail. | |
267 | ||
268 | [enums]: ch06-00-enums.html | |
269 | ||
270 | For `Result`, the variants are `Ok` or `Err`. The `Ok` variant indicates the | |
271 | operation was successful, and inside `Ok` is the successfully generated value. | |
272 | The `Err` variant means the operation failed, and `Err` contains information | |
273 | about how or why the operation failed. | |
274 | ||
275 | The purpose of these `Result` types is to encode error-handling information. | |
69743fb6 XL |
276 | Values of the `Result` type, like values of any type, have methods defined on |
277 | them. An instance of `io::Result` has an [`expect` method][expect]<!-- ignore | |
278 | --> that you can call. If this instance of `io::Result` is an `Err` value, | |
279 | `expect` will cause the program to crash and display the message that you | |
280 | passed as an argument to `expect`. If the `read_line` method returns an `Err`, | |
281 | it would likely be the result of an error coming from the underlying operating | |
282 | system. If this instance of `io::Result` is an `Ok` value, `expect` will take | |
283 | the return value that `Ok` is holding and return just that value to you so you | |
13cf67c4 XL |
284 | can use it. In this case, that value is the number of bytes in what the user |
285 | entered into standard input. | |
286 | ||
287 | [expect]: ../std/result/enum.Result.html#method.expect | |
288 | ||
289 | If you don’t call `expect`, the program will compile, but you’ll get a warning: | |
290 | ||
291 | ```text | |
292 | $ cargo build | |
293 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
294 | warning: unused `std::result::Result` which must be used | |
295 | --> src/main.rs:10:5 | |
296 | | | |
297 | 10 | io::stdin().read_line(&mut guess); | |
298 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
299 | | | |
300 | = note: #[warn(unused_must_use)] on by default | |
301 | ``` | |
302 | ||
303 | Rust warns that you haven’t used the `Result` value returned from `read_line`, | |
304 | indicating that the program hasn’t handled a possible error. | |
305 | ||
306 | The right way to suppress the warning is to actually write error handling, but | |
69743fb6 | 307 | because you just want to crash this program when a problem occurs, you can use |
13cf67c4 XL |
308 | `expect`. You’ll learn about recovering from errors in Chapter 9. |
309 | ||
310 | ### Printing Values with `println!` Placeholders | |
311 | ||
312 | Aside from the closing curly brackets, there’s only one more line to discuss in | |
313 | the code added so far, which is the following: | |
314 | ||
315 | ```rust,ignore | |
316 | println!("You guessed: {}", guess); | |
317 | ``` | |
318 | ||
319 | This line prints the string we saved the user’s input in. The set of curly | |
320 | brackets, `{}`, is a placeholder: think of `{}` as little crab pincers that | |
321 | hold a value in place. You can print more than one value using curly brackets: | |
322 | the first set of curly brackets holds the first value listed after the format | |
323 | string, the second set holds the second value, and so on. Printing multiple | |
324 | values in one call to `println!` would look like this: | |
325 | ||
326 | ```rust | |
327 | let x = 5; | |
328 | let y = 10; | |
329 | ||
330 | println!("x = {} and y = {}", x, y); | |
331 | ``` | |
332 | ||
333 | This code would print `x = 5 and y = 10`. | |
334 | ||
335 | ### Testing the First Part | |
336 | ||
337 | Let’s test the first part of the guessing game. Run it using `cargo run`: | |
338 | ||
339 | ```text | |
340 | $ cargo run | |
341 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
342 | Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs | |
343 | Running `target/debug/guessing_game` | |
344 | Guess the number! | |
345 | Please input your guess. | |
346 | 6 | |
347 | You guessed: 6 | |
348 | ``` | |
349 | ||
350 | At this point, the first part of the game is done: we’re getting input from the | |
351 | keyboard and then printing it. | |
352 | ||
353 | ## Generating a Secret Number | |
354 | ||
355 | Next, we need to generate a secret number that the user will try to guess. The | |
356 | secret number should be different every time so the game is fun to play more | |
357 | than once. Let’s use a random number between 1 and 100 so the game isn’t too | |
358 | difficult. Rust doesn’t yet include random number functionality in its standard | |
359 | library. However, the Rust team does provide a [`rand` crate][randcrate]. | |
360 | ||
361 | [randcrate]: https://crates.io/crates/rand | |
362 | ||
363 | ### Using a Crate to Get More Functionality | |
364 | ||
365 | Remember that a crate is a collection of Rust source code files. | |
366 | The project we’ve been building is a *binary crate*, which is an executable. | |
367 | The `rand` crate is a *library crate*, which contains code intended to be | |
368 | used in other programs. | |
369 | ||
370 | Cargo’s use of external crates is where it really shines. Before we can write | |
371 | code that uses `rand`, we need to modify the *Cargo.toml* file to include the | |
372 | `rand` crate as a dependency. Open that file now and add the following line to | |
373 | the bottom beneath the `[dependencies]` section header that Cargo created for | |
374 | you: | |
375 | ||
376 | <span class="filename">Filename: Cargo.toml</span> | |
377 | ||
378 | ```toml | |
379 | [dependencies] | |
380 | ||
381 | rand = "0.3.14" | |
382 | ``` | |
383 | ||
384 | In the *Cargo.toml* file, everything that follows a header is part of a section | |
385 | that continues until another section starts. The `[dependencies]` section is | |
386 | where you tell Cargo which external crates your project depends on and which | |
387 | versions of those crates you require. In this case, we’ll specify the `rand` | |
388 | crate with the semantic version specifier `0.3.14`. Cargo understands [Semantic | |
389 | Versioning][semver]<!-- ignore --> (sometimes called *SemVer*), which is a | |
390 | standard for writing version numbers. The number `0.3.14` is actually shorthand | |
391 | for `^0.3.14`, which means “any version that has a public API compatible with | |
392 | version 0.3.14.” | |
393 | ||
394 | [semver]: http://semver.org | |
395 | ||
396 | Now, without changing any of the code, let’s build the project, as shown in | |
69743fb6 | 397 | Listing 2-2. |
13cf67c4 XL |
398 | |
399 | ```text | |
400 | $ cargo build | |
401 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
402 | Downloading rand v0.3.14 | |
403 | Downloading libc v0.2.14 | |
404 | Compiling libc v0.2.14 | |
405 | Compiling rand v0.3.14 | |
406 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
407 | Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs | |
408 | ``` | |
409 | ||
410 | <span class="caption">Listing 2-2: The output from running `cargo build` after | |
411 | adding the rand crate as a dependency</span> | |
412 | ||
413 | You may see different version numbers (but they will all be compatible with | |
414 | the code, thanks to SemVer!), and the lines may be in a different order. | |
415 | ||
416 | Now that we have an external dependency, Cargo fetches the latest versions of | |
417 | everything from the *registry*, which is a copy of data from | |
418 | [Crates.io][cratesio]. Crates.io is where people in the Rust ecosystem post | |
419 | their open source Rust projects for others to use. | |
420 | ||
421 | [cratesio]: https://crates.io | |
422 | ||
423 | After updating the registry, Cargo checks the `[dependencies]` section and | |
424 | downloads any crates you don’t have yet. In this case, although we only listed | |
425 | `rand` as a dependency, Cargo also grabbed a copy of `libc`, because `rand` | |
426 | depends on `libc` to work. After downloading the crates, Rust compiles them and | |
427 | then compiles the project with the dependencies available. | |
428 | ||
429 | If you immediately run `cargo build` again without making any changes, you | |
430 | won’t get any output aside from the `Finished` line. Cargo knows it has already | |
431 | downloaded and compiled the dependencies, and you haven’t changed anything | |
432 | about them in your *Cargo.toml* file. Cargo also knows that you haven’t changed | |
433 | anything about your code, so it doesn’t recompile that either. With nothing to | |
434 | do, it simply exits. | |
435 | ||
436 | If you open up the *src/main.rs* file, make a trivial change, and then save it | |
437 | and build again, you’ll only see two lines of output: | |
438 | ||
439 | ```text | |
440 | $ cargo build | |
441 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
442 | Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs | |
443 | ``` | |
444 | ||
445 | These lines show Cargo only updates the build with your tiny change to the | |
446 | *src/main.rs* file. Your dependencies haven’t changed, so Cargo knows it can | |
447 | reuse what it has already downloaded and compiled for those. It just rebuilds | |
448 | your part of the code. | |
449 | ||
450 | #### Ensuring Reproducible Builds with the *Cargo.lock* File | |
451 | ||
452 | Cargo has a mechanism that ensures you can rebuild the same artifact every time | |
453 | you or anyone else builds your code: Cargo will use only the versions of the | |
454 | dependencies you specified until you indicate otherwise. For example, what | |
69743fb6 | 455 | happens if next week version 0.3.15 of the `rand` crate comes out and |
13cf67c4 XL |
456 | contains an important bug fix but also contains a regression that will break |
457 | your code? | |
458 | ||
459 | The answer to this problem is the *Cargo.lock* file, which was created the | |
460 | first time you ran `cargo build` and is now in your *guessing_game* directory. | |
461 | When you build a project for the first time, Cargo figures out all the | |
462 | versions of the dependencies that fit the criteria and then writes them to | |
463 | the *Cargo.lock* file. When you build your project in the future, Cargo will | |
464 | see that the *Cargo.lock* file exists and use the versions specified there | |
465 | rather than doing all the work of figuring out versions again. This lets you | |
466 | have a reproducible build automatically. In other words, your project will | |
467 | remain at `0.3.14` until you explicitly upgrade, thanks to the *Cargo.lock* | |
468 | file. | |
469 | ||
470 | #### Updating a Crate to Get a New Version | |
471 | ||
472 | When you *do* want to update a crate, Cargo provides another command, `update`, | |
473 | which will ignore the *Cargo.lock* file and figure out all the latest versions | |
474 | that fit your specifications in *Cargo.toml*. If that works, Cargo will write | |
475 | those versions to the *Cargo.lock* file. | |
476 | ||
9fa01778 XL |
477 | But by default, Cargo will only look for versions greater than `0.3.0` and less |
478 | than `0.4.0`. If the `rand` crate has released two new versions, `0.3.15` and | |
479 | `0.4.0`, you would see the following if you ran `cargo update`: | |
13cf67c4 XL |
480 | |
481 | ```text | |
482 | $ cargo update | |
483 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
484 | Updating rand v0.3.14 -> v0.3.15 | |
485 | ``` | |
486 | ||
487 | At this point, you would also notice a change in your *Cargo.lock* file noting | |
488 | that the version of the `rand` crate you are now using is `0.3.15`. | |
489 | ||
490 | If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x` | |
491 | series, you’d have to update the *Cargo.toml* file to look like this instead: | |
492 | ||
493 | ```toml | |
494 | [dependencies] | |
495 | ||
496 | rand = "0.4.0" | |
497 | ``` | |
498 | ||
499 | The next time you run `cargo build`, Cargo will update the registry of crates | |
500 | available and reevaluate your `rand` requirements according to the new version | |
501 | you have specified. | |
502 | ||
503 | There’s a lot more to say about [Cargo][doccargo]<!-- ignore --> and [its | |
504 | ecosystem][doccratesio]<!-- ignore --> which we’ll discuss in Chapter 14, but | |
505 | for now, that’s all you need to know. Cargo makes it very easy to reuse | |
506 | libraries, so Rustaceans are able to write smaller projects that are assembled | |
507 | from a number of packages. | |
508 | ||
509 | [doccargo]: http://doc.crates.io | |
510 | [doccratesio]: http://doc.crates.io/crates-io.html | |
511 | ||
512 | ### Generating a Random Number | |
513 | ||
514 | Now that you’ve added the `rand` crate to *Cargo.toml*, let’s start using | |
69743fb6 | 515 | `rand`. The next step is to update *src/main.rs*, as shown in Listing 2-3. |
13cf67c4 XL |
516 | |
517 | <span class="filename">Filename: src/main.rs</span> | |
518 | ||
519 | ```rust,ignore | |
520 | use std::io; | |
521 | use rand::Rng; | |
522 | ||
523 | fn main() { | |
524 | println!("Guess the number!"); | |
525 | ||
526 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
527 | ||
528 | println!("The secret number is: {}", secret_number); | |
529 | ||
530 | println!("Please input your guess."); | |
531 | ||
532 | let mut guess = String::new(); | |
533 | ||
534 | io::stdin().read_line(&mut guess) | |
535 | .expect("Failed to read line"); | |
536 | ||
537 | println!("You guessed: {}", guess); | |
538 | } | |
539 | ``` | |
540 | ||
541 | <span class="caption">Listing 2-3: Adding code to generate a random | |
542 | number</span> | |
543 | ||
9fa01778 | 544 | First, we add a `use` line: `use rand::Rng`. The `Rng` trait defines |
13cf67c4 XL |
545 | methods that random number generators implement, and this trait must be in |
546 | scope for us to use those methods. Chapter 10 will cover traits in detail. | |
547 | ||
9fa01778 | 548 | Next, we’re adding two lines in the middle. The `rand::thread_rng` function |
13cf67c4 XL |
549 | will give us the particular random number generator that we’re going to use: |
550 | one that is local to the current thread of execution and seeded by the | |
9fa01778 | 551 | operating system. Then we call the `gen_range` method on the random number |
13cf67c4 XL |
552 | generator. This method is defined by the `Rng` trait that we brought into |
553 | scope with the `use rand::Rng` statement. The `gen_range` method takes two | |
554 | numbers as arguments and generates a random number between them. It’s inclusive | |
555 | on the lower bound but exclusive on the upper bound, so we need to specify `1` | |
556 | and `101` to request a number between 1 and 100. | |
557 | ||
558 | > Note: You won’t just know which traits to use and which methods and functions | |
559 | > to call from a crate. Instructions for using a crate are in each crate’s | |
560 | > documentation. Another neat feature of Cargo is that you can run the `cargo | |
561 | > doc --open` command, which will build documentation provided by all of your | |
562 | > dependencies locally and open it in your browser. If you’re interested in | |
563 | > other functionality in the `rand` crate, for example, run `cargo doc --open` | |
564 | > and click `rand` in the sidebar on the left. | |
565 | ||
9fa01778 XL |
566 | The second line that we added to the middle of the code prints the secret |
567 | number. This is useful while we’re developing the program to be able to test | |
568 | it, but we’ll delete it from the final version. It’s not much of a game if the | |
569 | program prints the answer as soon as it starts! | |
13cf67c4 XL |
570 | |
571 | Try running the program a few times: | |
572 | ||
573 | ```text | |
574 | $ cargo run | |
575 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
576 | Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs | |
577 | Running `target/debug/guessing_game` | |
578 | Guess the number! | |
579 | The secret number is: 7 | |
580 | Please input your guess. | |
581 | 4 | |
582 | You guessed: 4 | |
583 | $ cargo run | |
584 | Running `target/debug/guessing_game` | |
585 | Guess the number! | |
586 | The secret number is: 83 | |
587 | Please input your guess. | |
588 | 5 | |
589 | You guessed: 5 | |
590 | ``` | |
591 | ||
592 | You should get different random numbers, and they should all be numbers between | |
593 | 1 and 100. Great job! | |
594 | ||
595 | ## Comparing the Guess to the Secret Number | |
596 | ||
597 | Now that we have user input and a random number, we can compare them. That step | |
598 | is shown in Listing 2-4. Note that this code won’t compile quite yet, as we | |
599 | will explain. | |
600 | ||
601 | <span class="filename">Filename: src/main.rs</span> | |
602 | ||
603 | ```rust,ignore,does_not_compile | |
604 | use std::io; | |
605 | use std::cmp::Ordering; | |
606 | use rand::Rng; | |
607 | ||
608 | fn main() { | |
69743fb6 | 609 | |
13cf67c4 XL |
610 | // ---snip--- |
611 | ||
612 | println!("You guessed: {}", guess); | |
613 | ||
614 | match guess.cmp(&secret_number) { | |
615 | Ordering::Less => println!("Too small!"), | |
616 | Ordering::Greater => println!("Too big!"), | |
617 | Ordering::Equal => println!("You win!"), | |
618 | } | |
619 | } | |
620 | ``` | |
621 | ||
622 | <span class="caption">Listing 2-4: Handling the possible return values of | |
623 | comparing two numbers</span> | |
624 | ||
625 | The first new bit here is another `use` statement, bringing a type called | |
626 | `std::cmp::Ordering` into scope from the standard library. Like `Result`, | |
627 | `Ordering` is another enum, but the variants for `Ordering` are `Less`, | |
628 | `Greater`, and `Equal`. These are the three outcomes that are possible when you | |
629 | compare two values. | |
630 | ||
69743fb6 XL |
631 | Then we add five new lines at the bottom that use the `Ordering` type. The |
632 | `cmp` method compares two values and can be called on anything that can be | |
13cf67c4 XL |
633 | compared. It takes a reference to whatever you want to compare with: here it’s |
634 | comparing the `guess` to the `secret_number`. Then it returns a variant of the | |
635 | `Ordering` enum we brought into scope with the `use` statement. We use a | |
636 | [`match`][match]<!-- ignore --> expression to decide what to do next based on | |
637 | which variant of `Ordering` was returned from the call to `cmp` with the values | |
638 | in `guess` and `secret_number`. | |
639 | ||
640 | [match]: ch06-02-match.html | |
641 | ||
642 | A `match` expression is made up of *arms*. An arm consists of a *pattern* and | |
643 | the code that should be run if the value given to the beginning of the `match` | |
644 | expression fits that arm’s pattern. Rust takes the value given to `match` and | |
645 | looks through each arm’s pattern in turn. The `match` construct and patterns | |
646 | are powerful features in Rust that let you express a variety of situations your | |
647 | code might encounter and make sure that you handle them all. These features | |
648 | will be covered in detail in Chapter 6 and Chapter 18, respectively. | |
649 | ||
650 | Let’s walk through an example of what would happen with the `match` expression | |
651 | used here. Say that the user has guessed 50 and the randomly generated secret | |
652 | number this time is 38. When the code compares 50 to 38, the `cmp` method will | |
653 | return `Ordering::Greater`, because 50 is greater than 38. The `match` | |
654 | expression gets the `Ordering::Greater` value and starts checking each arm’s | |
655 | pattern. It looks at the first arm’s pattern, `Ordering::Less`, and sees that | |
656 | the value `Ordering::Greater` does not match `Ordering::Less`, so it ignores | |
657 | the code in that arm and moves to the next arm. The next arm’s pattern, | |
658 | `Ordering::Greater`, *does* match `Ordering::Greater`! The associated code in | |
659 | that arm will execute and print `Too big!` to the screen. The `match` | |
660 | expression ends because it has no need to look at the last arm in this scenario. | |
661 | ||
662 | However, the code in Listing 2-4 won’t compile yet. Let’s try it: | |
663 | ||
664 | ```text | |
665 | $ cargo build | |
666 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
667 | error[E0308]: mismatched types | |
668 | --> src/main.rs:23:21 | |
669 | | | |
670 | 23 | match guess.cmp(&secret_number) { | |
671 | | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable | |
672 | | | |
673 | = note: expected type `&std::string::String` | |
674 | = note: found type `&{integer}` | |
675 | ||
676 | error: aborting due to previous error | |
677 | Could not compile `guessing_game`. | |
678 | ``` | |
679 | ||
680 | The core of the error states that there are *mismatched types*. Rust has a | |
681 | strong, static type system. However, it also has type inference. When we wrote | |
9fa01778 | 682 | `let mut guess = String::new()`, Rust was able to infer that `guess` should be |
13cf67c4 XL |
683 | a `String` and didn’t make us write the type. The `secret_number`, on the other |
684 | hand, is a number type. A few number types can have a value between 1 and 100: | |
685 | `i32`, a 32-bit number; `u32`, an unsigned 32-bit number; `i64`, a 64-bit | |
686 | number; as well as others. Rust defaults to an `i32`, which is the type of | |
687 | `secret_number` unless you add type information elsewhere that would cause Rust | |
688 | to infer a different numerical type. The reason for the error is that Rust | |
689 | cannot compare a string and a number type. | |
690 | ||
691 | Ultimately, we want to convert the `String` the program reads as input into a | |
9fa01778 XL |
692 | real number type so we can compare it numerically to the secret number. We can |
693 | do that by adding the following two lines to the `main` function body: | |
13cf67c4 XL |
694 | |
695 | <span class="filename">Filename: src/main.rs</span> | |
696 | ||
697 | ```rust,ignore | |
698 | // --snip-- | |
699 | ||
700 | let mut guess = String::new(); | |
701 | ||
702 | io::stdin().read_line(&mut guess) | |
703 | .expect("Failed to read line"); | |
704 | ||
705 | let guess: u32 = guess.trim().parse() | |
706 | .expect("Please type a number!"); | |
707 | ||
708 | println!("You guessed: {}", guess); | |
709 | ||
710 | match guess.cmp(&secret_number) { | |
711 | Ordering::Less => println!("Too small!"), | |
712 | Ordering::Greater => println!("Too big!"), | |
713 | Ordering::Equal => println!("You win!"), | |
714 | } | |
715 | } | |
716 | ``` | |
717 | ||
718 | The two new lines are: | |
719 | ||
720 | ```rust,ignore | |
721 | let guess: u32 = guess.trim().parse() | |
722 | .expect("Please type a number!"); | |
723 | ``` | |
724 | ||
725 | We create a variable named `guess`. But wait, doesn’t the program already have | |
726 | a variable named `guess`? It does, but Rust allows us to *shadow* the previous | |
727 | value of `guess` with a new one. This feature is often used in situations in | |
728 | which you want to convert a value from one type to another type. Shadowing lets | |
729 | us reuse the `guess` variable name rather than forcing us to create two unique | |
69743fb6 | 730 | variables, such as `guess_str` and `guess` for example. (Chapter 3 covers |
13cf67c4 XL |
731 | shadowing in more detail.) |
732 | ||
733 | We bind `guess` to the expression `guess.trim().parse()`. The `guess` in the | |
734 | expression refers to the original `guess` that was a `String` with the input in | |
735 | it. The `trim` method on a `String` instance will eliminate any whitespace at | |
736 | the beginning and end. Although `u32` can contain only numerical characters, | |
737 | the user must press <span class="keystroke">enter</span> to satisfy | |
738 | `read_line`. When the user presses <span class="keystroke">enter</span>, a | |
739 | newline character is added to the string. For example, if the user types <span | |
740 | class="keystroke">5</span> and presses <span class="keystroke">enter</span>, | |
741 | `guess` looks like this: `5\n`. The `\n` represents “newline,” the result of | |
742 | pressing <span class="keystroke">enter</span>. The `trim` method eliminates | |
743 | `\n`, resulting in just `5`. | |
744 | ||
745 | The [`parse` method on strings][parse]<!-- ignore --> parses a string into some | |
746 | kind of number. Because this method can parse a variety of number types, we | |
747 | need to tell Rust the exact number type we want by using `let guess: u32`. The | |
748 | colon (`:`) after `guess` tells Rust we’ll annotate the variable’s type. Rust | |
749 | has a few built-in number types; the `u32` seen here is an unsigned, 32-bit | |
750 | integer. It’s a good default choice for a small positive number. You’ll learn | |
751 | about other number types in Chapter 3. Additionally, the `u32` annotation in | |
752 | this example program and the comparison with `secret_number` means that Rust | |
753 | will infer that `secret_number` should be a `u32` as well. So now the | |
754 | comparison will be between two values of the same type! | |
755 | ||
756 | [parse]: ../std/primitive.str.html#method.parse | |
757 | ||
758 | The call to `parse` could easily cause an error. If, for example, the string | |
759 | contained `A👍%`, there would be no way to convert that to a number. Because it | |
760 | might fail, the `parse` method returns a `Result` type, much as the `read_line` | |
9fa01778 XL |
761 | method does (discussed earlier in [“Handling Potential Failure with the |
762 | `Result` Type”](#handling-potential-failure-with-the-result-type)<!-- ignore | |
763 | -->). We’ll treat this `Result` the same way by using the `expect` method | |
13cf67c4 XL |
764 | again. If `parse` returns an `Err` `Result` variant because it couldn’t create |
765 | a number from the string, the `expect` call will crash the game and print the | |
766 | message we give it. If `parse` can successfully convert the string to a number, | |
767 | it will return the `Ok` variant of `Result`, and `expect` will return the | |
768 | number that we want from the `Ok` value. | |
769 | ||
770 | Let’s run the program now! | |
771 | ||
772 | ```text | |
773 | $ cargo run | |
774 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
775 | Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs | |
69743fb6 | 776 | Running `target/debug/guessing_game` |
13cf67c4 XL |
777 | Guess the number! |
778 | The secret number is: 58 | |
779 | Please input your guess. | |
780 | 76 | |
781 | You guessed: 76 | |
782 | Too big! | |
783 | ``` | |
784 | ||
785 | Nice! Even though spaces were added before the guess, the program still figured | |
786 | out that the user guessed 76. Run the program a few times to verify the | |
787 | different behavior with different kinds of input: guess the number correctly, | |
788 | guess a number that is too high, and guess a number that is too low. | |
789 | ||
790 | We have most of the game working now, but the user can make only one guess. | |
791 | Let’s change that by adding a loop! | |
792 | ||
793 | ## Allowing Multiple Guesses with Looping | |
794 | ||
795 | The `loop` keyword creates an infinite loop. We’ll add that now to give users | |
796 | more chances at guessing the number: | |
797 | ||
798 | <span class="filename">Filename: src/main.rs</span> | |
799 | ||
800 | ```rust,ignore | |
801 | // --snip-- | |
802 | ||
803 | println!("The secret number is: {}", secret_number); | |
804 | ||
805 | loop { | |
806 | println!("Please input your guess."); | |
807 | ||
808 | // --snip-- | |
809 | ||
810 | match guess.cmp(&secret_number) { | |
811 | Ordering::Less => println!("Too small!"), | |
812 | Ordering::Greater => println!("Too big!"), | |
813 | Ordering::Equal => println!("You win!"), | |
814 | } | |
815 | } | |
816 | } | |
817 | ``` | |
818 | ||
819 | As you can see, we’ve moved everything into a loop from the guess input prompt | |
820 | onward. Be sure to indent the lines inside the loop another four spaces each | |
821 | and run the program again. Notice that there is a new problem because the | |
822 | program is doing exactly what we told it to do: ask for another guess forever! | |
823 | It doesn’t seem like the user can quit! | |
824 | ||
825 | The user could always halt the program by using the keyboard shortcut <span | |
826 | class="keystroke">ctrl-c</span>. But there’s another way to escape this | |
9fa01778 XL |
827 | insatiable monster, as mentioned in the `parse` discussion in [“Comparing the |
828 | Guess to the Secret Number”](#comparing-the-guess-to-the-secret-number)<!-- | |
829 | ignore -->: if the user enters a non-number answer, the program will crash. The | |
830 | user can take advantage of that in order to quit, as shown here: | |
13cf67c4 XL |
831 | |
832 | ```text | |
833 | $ cargo run | |
834 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
835 | Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs | |
69743fb6 | 836 | Running `target/debug/guessing_game` |
13cf67c4 XL |
837 | Guess the number! |
838 | The secret number is: 59 | |
839 | Please input your guess. | |
840 | 45 | |
841 | You guessed: 45 | |
842 | Too small! | |
843 | Please input your guess. | |
844 | 60 | |
845 | You guessed: 60 | |
846 | Too big! | |
847 | Please input your guess. | |
848 | 59 | |
849 | You guessed: 59 | |
850 | You win! | |
851 | Please input your guess. | |
852 | quit | |
853 | thread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:785 | |
854 | note: Run with `RUST_BACKTRACE=1` for a backtrace. | |
855 | error: Process didn't exit successfully: `target/debug/guess` (exit code: 101) | |
856 | ``` | |
857 | ||
858 | Typing `quit` actually quits the game, but so will any other non-number input. | |
859 | However, this is suboptimal to say the least. We want the game to automatically | |
860 | stop when the correct number is guessed. | |
861 | ||
862 | ### Quitting After a Correct Guess | |
863 | ||
864 | Let’s program the game to quit when the user wins by adding a `break` statement: | |
865 | ||
866 | <span class="filename">Filename: src/main.rs</span> | |
867 | ||
868 | ```rust,ignore | |
869 | // --snip-- | |
870 | ||
871 | match guess.cmp(&secret_number) { | |
872 | Ordering::Less => println!("Too small!"), | |
873 | Ordering::Greater => println!("Too big!"), | |
874 | Ordering::Equal => { | |
875 | println!("You win!"); | |
876 | break; | |
877 | } | |
878 | } | |
879 | } | |
880 | } | |
881 | ``` | |
882 | ||
883 | Adding the `break` line after `You win!` makes the program exit the loop when | |
884 | the user guesses the secret number correctly. Exiting the loop also means | |
885 | exiting the program, because the loop is the last part of `main`. | |
886 | ||
887 | ### Handling Invalid Input | |
888 | ||
889 | To further refine the game’s behavior, rather than crashing the program when | |
890 | the user inputs a non-number, let’s make the game ignore a non-number so the | |
891 | user can continue guessing. We can do that by altering the line where `guess` | |
69743fb6 XL |
892 | is converted from a `String` to a `u32`, as shown in Listing 2-5. |
893 | ||
894 | <span class="filename">Filename: src/main.rs</span> | |
13cf67c4 XL |
895 | |
896 | ```rust,ignore | |
69743fb6 XL |
897 | // --snip-- |
898 | ||
899 | io::stdin().read_line(&mut guess) | |
900 | .expect("Failed to read line"); | |
901 | ||
13cf67c4 XL |
902 | let guess: u32 = match guess.trim().parse() { |
903 | Ok(num) => num, | |
904 | Err(_) => continue, | |
905 | }; | |
69743fb6 XL |
906 | |
907 | println!("You guessed: {}", guess); | |
908 | ||
909 | // --snip-- | |
13cf67c4 XL |
910 | ``` |
911 | ||
69743fb6 XL |
912 | <span class="caption">Listing 2-5: Ignoring a non-number guess and asking for |
913 | another guess instead of crashing the program</span> | |
914 | ||
13cf67c4 XL |
915 | Switching from an `expect` call to a `match` expression is how you generally |
916 | move from crashing on an error to handling the error. Remember that `parse` | |
917 | returns a `Result` type and `Result` is an enum that has the variants `Ok` or | |
918 | `Err`. We’re using a `match` expression here, as we did with the `Ordering` | |
919 | result of the `cmp` method. | |
920 | ||
921 | If `parse` is able to successfully turn the string into a number, it will | |
922 | return an `Ok` value that contains the resulting number. That `Ok` value will | |
923 | match the first arm’s pattern, and the `match` expression will just return the | |
924 | `num` value that `parse` produced and put inside the `Ok` value. That number | |
925 | will end up right where we want it in the new `guess` variable we’re creating. | |
926 | ||
927 | If `parse` is *not* able to turn the string into a number, it will return an | |
928 | `Err` value that contains more information about the error. The `Err` value | |
929 | does not match the `Ok(num)` pattern in the first `match` arm, but it does | |
930 | match the `Err(_)` pattern in the second arm. The underscore, `_`, is a | |
931 | catchall value; in this example, we’re saying we want to match all `Err` | |
932 | values, no matter what information they have inside them. So the program will | |
69743fb6 | 933 | execute the second arm’s code, `continue`, which tells the program to go to the |
9fa01778 | 934 | next iteration of the `loop` and ask for another guess. So, effectively, the |
69743fb6 | 935 | program ignores all errors that `parse` might encounter! |
13cf67c4 XL |
936 | |
937 | Now everything in the program should work as expected. Let’s try it: | |
938 | ||
939 | ```text | |
940 | $ cargo run | |
941 | Compiling guessing_game v0.1.0 (file:///projects/guessing_game) | |
69743fb6 | 942 | Running `target/debug/guessing_game` |
13cf67c4 XL |
943 | Guess the number! |
944 | The secret number is: 61 | |
945 | Please input your guess. | |
946 | 10 | |
947 | You guessed: 10 | |
948 | Too small! | |
949 | Please input your guess. | |
950 | 99 | |
951 | You guessed: 99 | |
952 | Too big! | |
953 | Please input your guess. | |
954 | foo | |
955 | Please input your guess. | |
956 | 61 | |
957 | You guessed: 61 | |
958 | You win! | |
959 | ``` | |
960 | ||
961 | Awesome! With one tiny final tweak, we will finish the guessing game. Recall | |
962 | that the program is still printing the secret number. That worked well for | |
963 | testing, but it ruins the game. Let’s delete the `println!` that outputs the | |
69743fb6 | 964 | secret number. Listing 2-6 shows the final code. |
13cf67c4 XL |
965 | |
966 | <span class="filename">Filename: src/main.rs</span> | |
967 | ||
968 | ```rust,ignore | |
969 | use std::io; | |
970 | use std::cmp::Ordering; | |
971 | use rand::Rng; | |
972 | ||
973 | fn main() { | |
974 | println!("Guess the number!"); | |
975 | ||
976 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
977 | ||
978 | loop { | |
979 | println!("Please input your guess."); | |
980 | ||
981 | let mut guess = String::new(); | |
982 | ||
983 | io::stdin().read_line(&mut guess) | |
984 | .expect("Failed to read line"); | |
985 | ||
986 | let guess: u32 = match guess.trim().parse() { | |
987 | Ok(num) => num, | |
988 | Err(_) => continue, | |
989 | }; | |
990 | ||
991 | println!("You guessed: {}", guess); | |
992 | ||
993 | match guess.cmp(&secret_number) { | |
994 | Ordering::Less => println!("Too small!"), | |
995 | Ordering::Greater => println!("Too big!"), | |
996 | Ordering::Equal => { | |
997 | println!("You win!"); | |
998 | break; | |
999 | } | |
1000 | } | |
1001 | } | |
1002 | } | |
1003 | ``` | |
1004 | ||
69743fb6 | 1005 | <span class="caption">Listing 2-6: Complete guessing game code</span> |
13cf67c4 XL |
1006 | |
1007 | ## Summary | |
1008 | ||
69743fb6 | 1009 | At this point, you’ve successfully built the guessing game. Congratulations! |
13cf67c4 XL |
1010 | |
1011 | This project was a hands-on way to introduce you to many new Rust concepts: | |
1012 | `let`, `match`, methods, associated functions, the use of external crates, and | |
1013 | more. In the next few chapters, you’ll learn about these concepts in more | |
1014 | detail. Chapter 3 covers concepts that most programming languages have, such as | |
1015 | variables, data types, and functions, and shows how to use them in Rust. | |
1016 | Chapter 4 explores ownership, a feature that makes Rust different from other | |
1017 | languages. Chapter 5 discusses structs and method syntax, and Chapter 6 | |
1018 | explains how enums work. | |
9fa01778 XL |
1019 | |
1020 | [variables-and-mutability]: | |
1021 | ch03-01-variables-and-mutability.html#variables-and-mutability |