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