]>
Commit | Line | Data |
---|---|---|
bd371182 AL |
1 | % Guessing Game |
2 | ||
3 | For our first project, we’ll implement a classic beginner programming problem: | |
4 | the guessing game. Here’s how it works: Our program will generate a random | |
5 | integer between one and a hundred. It will then prompt us to enter a guess. | |
6 | Upon entering our guess, it will tell us if we’re too low or too high. Once we | |
7 | guess correctly, it will congratulate us. Sounds good? | |
8 | ||
9 | # Set up | |
10 | ||
11 | Let’s set up a new project. Go to your projects directory. Remember how we had | |
12 | to create our directory structure and a `Cargo.toml` for `hello_world`? Cargo | |
13 | has a command that does that for us. Let’s give it a shot: | |
14 | ||
15 | ```bash | |
16 | $ cd ~/projects | |
17 | $ cargo new guessing_game --bin | |
18 | $ cd guessing_game | |
19 | ``` | |
20 | ||
21 | We pass the name of our project to `cargo new`, and then the `--bin` flag, | |
22 | since we’re making a binary, rather than a library. | |
23 | ||
24 | Check out the generated `Cargo.toml`: | |
25 | ||
26 | ```toml | |
27 | [package] | |
28 | ||
29 | name = "guessing_game" | |
d9579d0f | 30 | version = "0.1.0" |
bd371182 AL |
31 | authors = ["Your Name <you@example.com>"] |
32 | ``` | |
33 | ||
34 | Cargo gets this information from your environment. If it’s not correct, go ahead | |
35 | and fix that. | |
36 | ||
37 | Finally, Cargo generated a ‘Hello, world!’ for us. Check out `src/main.rs`: | |
38 | ||
39 | ```rust | |
40 | fn main() { | |
62682a34 | 41 | println!("Hello, world!"); |
bd371182 AL |
42 | } |
43 | ``` | |
44 | ||
45 | Let’s try compiling what Cargo gave us: | |
46 | ||
47 | ```{bash} | |
48 | $ cargo build | |
d9579d0f | 49 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) |
bd371182 AL |
50 | ``` |
51 | ||
52 | Excellent! Open up your `src/main.rs` again. We’ll be writing all of | |
53 | our code in this file. | |
54 | ||
55 | Before we move on, let me show you one more Cargo command: `run`. `cargo run` | |
56 | is kind of like `cargo build`, but it also then runs the produced executable. | |
57 | Try it out: | |
58 | ||
59 | ```bash | |
60 | $ cargo run | |
d9579d0f | 61 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) |
bd371182 AL |
62 | Running `target/debug/guessing_game` |
63 | Hello, world! | |
64 | ``` | |
65 | ||
66 | Great! The `run` command comes in handy when you need to rapidly iterate on a | |
67 | project. Our game is just such a project, we need to quickly test each | |
68 | iteration before moving on to the next one. | |
69 | ||
70 | # Processing a Guess | |
71 | ||
72 | Let’s get to it! The first thing we need to do for our guessing game is | |
73 | allow our player to input a guess. Put this in your `src/main.rs`: | |
74 | ||
75 | ```rust,no_run | |
76 | use std::io; | |
77 | ||
78 | fn main() { | |
79 | println!("Guess the number!"); | |
80 | ||
81 | println!("Please input your guess."); | |
82 | ||
83 | let mut guess = String::new(); | |
84 | ||
85 | io::stdin().read_line(&mut guess) | |
86 | .ok() | |
87 | .expect("Failed to read line"); | |
88 | ||
89 | println!("You guessed: {}", guess); | |
90 | } | |
91 | ``` | |
92 | ||
93 | There’s a lot here! Let’s go over it, bit by bit. | |
94 | ||
95 | ```rust,ignore | |
96 | use std::io; | |
97 | ``` | |
98 | ||
99 | We’ll need to take user input, and then print the result as output. As such, we | |
100 | need the `io` library from the standard library. Rust only imports a few things | |
c1a9b12d | 101 | by default into every program, [the ‘prelude’][prelude]. If it’s not in the |
b039eaaf SL |
102 | prelude, you’ll have to `use` it directly. There is also a second ‘prelude’, the |
103 | [`io` prelude][ioprelude], which serves a similar function: you import it, and it | |
104 | imports a number of useful, `io`-related things. | |
bd371182 AL |
105 | |
106 | [prelude]: ../std/prelude/index.html | |
b039eaaf | 107 | [ioprelude]: ../std/io/prelude/index.html |
bd371182 AL |
108 | |
109 | ```rust,ignore | |
110 | fn main() { | |
111 | ``` | |
112 | ||
113 | As you’ve seen before, the `main()` function is the entry point into your | |
114 | program. The `fn` syntax declares a new function, the `()`s indicate that | |
115 | there are no arguments, and `{` starts the body of the function. Because | |
116 | we didn’t include a return type, it’s assumed to be `()`, an empty | |
117 | [tuple][tuples]. | |
118 | ||
119 | [tuples]: primitive-types.html#tuples | |
120 | ||
121 | ```rust,ignore | |
122 | println!("Guess the number!"); | |
123 | ||
124 | println!("Please input your guess."); | |
125 | ``` | |
126 | ||
127 | We previously learned that `println!()` is a [macro][macros] that | |
128 | prints a [string][strings] to the screen. | |
129 | ||
130 | [macros]: macros.html | |
131 | [strings]: strings.html | |
132 | ||
133 | ```rust,ignore | |
134 | let mut guess = String::new(); | |
135 | ``` | |
136 | ||
137 | Now we’re getting interesting! There’s a lot going on in this little line. | |
138 | The first thing to notice is that this is a [let statement][let], which is | |
139 | used to create ‘variable bindings’. They take this form: | |
140 | ||
141 | ```rust,ignore | |
142 | let foo = bar; | |
143 | ``` | |
144 | ||
145 | [let]: variable-bindings.html | |
146 | ||
147 | This will create a new binding named `foo`, and bind it to the value `bar`. In | |
148 | many languages, this is called a ‘variable’, but Rust’s variable bindings have | |
149 | a few tricks up their sleeves. | |
150 | ||
151 | For example, they’re [immutable][immutable] by default. That’s why our example | |
152 | uses `mut`: it makes a binding mutable, rather than immutable. `let` doesn’t | |
b039eaaf | 153 | take a name on the left hand side of the assignment, it actually accepts a |
62682a34 | 154 | ‘[pattern][patterns]’. We’ll use patterns later. It’s easy enough |
bd371182 AL |
155 | to use for now: |
156 | ||
62682a34 | 157 | ```rust |
bd371182 AL |
158 | let foo = 5; // immutable. |
159 | let mut bar = 5; // mutable | |
160 | ``` | |
161 | ||
162 | [immutable]: mutability.html | |
163 | [patterns]: patterns.html | |
164 | ||
165 | Oh, and `//` will start a comment, until the end of the line. Rust ignores | |
166 | everything in [comments][comments]. | |
167 | ||
168 | [comments]: comments.html | |
169 | ||
170 | So now we know that `let mut guess` will introduce a mutable binding named | |
171 | `guess`, but we have to look at the other side of the `=` for what it’s | |
172 | bound to: `String::new()`. | |
173 | ||
174 | `String` is a string type, provided by the standard library. A | |
175 | [`String`][string] is a growable, UTF-8 encoded bit of text. | |
176 | ||
177 | [string]: ../std/string/struct.String.html | |
178 | ||
179 | The `::new()` syntax uses `::` because this is an ‘associated function’ of | |
180 | a particular type. That is to say, it’s associated with `String` itself, | |
181 | rather than a particular instance of a `String`. Some languages call this a | |
182 | ‘static method’. | |
183 | ||
184 | This function is named `new()`, because it creates a new, empty `String`. | |
185 | You’ll find a `new()` function on many types, as it’s a common name for making | |
186 | a new value of some kind. | |
187 | ||
188 | Let’s move forward: | |
189 | ||
190 | ```rust,ignore | |
191 | io::stdin().read_line(&mut guess) | |
192 | .ok() | |
193 | .expect("Failed to read line"); | |
194 | ``` | |
195 | ||
196 | That’s a lot more! Let’s go bit-by-bit. The first line has two parts. Here’s | |
197 | the first: | |
198 | ||
199 | ```rust,ignore | |
200 | io::stdin() | |
201 | ``` | |
202 | ||
203 | Remember how we `use`d `std::io` on the first line of the program? We’re now | |
204 | calling an associated function on it. If we didn’t `use std::io`, we could | |
205 | have written this line as `std::io::stdin()`. | |
206 | ||
207 | This particular function returns a handle to the standard input for your | |
208 | terminal. More specifically, a [std::io::Stdin][iostdin]. | |
209 | ||
210 | [iostdin]: ../std/io/struct.Stdin.html | |
211 | ||
212 | The next part will use this handle to get input from the user: | |
213 | ||
214 | ```rust,ignore | |
215 | .read_line(&mut guess) | |
216 | ``` | |
217 | ||
218 | Here, we call the [`read_line()`][read_line] method on our handle. | |
d9579d0f | 219 | [Methods][method] are like associated functions, but are only available on a |
bd371182 AL |
220 | particular instance of a type, rather than the type itself. We’re also passing |
221 | one argument to `read_line()`: `&mut guess`. | |
222 | ||
223 | [read_line]: ../std/io/struct.Stdin.html#method.read_line | |
d9579d0f | 224 | [method]: method-syntax.html |
bd371182 AL |
225 | |
226 | Remember how we bound `guess` above? We said it was mutable. However, | |
227 | `read_line` doesn’t take a `String` as an argument: it takes a `&mut String`. | |
228 | Rust has a feature called ‘[references][references]’, which allows you to have | |
229 | multiple references to one piece of data, which can reduce copying. References | |
230 | are a complex feature, as one of Rust’s major selling points is how safe and | |
231 | easy it is to use references. We don’t need to know a lot of those details to | |
232 | finish our program right now, though. For now, all we need to know is that | |
233 | like `let` bindings, references are immutable by default. Hence, we need to | |
234 | write `&mut guess`, rather than `&guess`. | |
235 | ||
236 | Why does `read_line()` take a mutable reference to a string? Its job is | |
237 | to take what the user types into standard input, and place that into a | |
238 | string. So it takes that string as an argument, and in order to add | |
239 | the input, it needs to be mutable. | |
240 | ||
241 | [references]: references-and-borrowing.html | |
242 | ||
243 | But we’re not quite done with this line of code, though. While it’s | |
244 | a single line of text, it’s only the first part of the single logical line of | |
245 | code: | |
246 | ||
247 | ```rust,ignore | |
248 | .ok() | |
249 | .expect("Failed to read line"); | |
250 | ``` | |
251 | ||
252 | When you call a method with the `.foo()` syntax, you may introduce a newline | |
253 | and other whitespace. This helps you split up long lines. We _could_ have | |
254 | done: | |
255 | ||
256 | ```rust,ignore | |
257 | io::stdin().read_line(&mut guess).ok().expect("failed to read line"); | |
258 | ``` | |
259 | ||
260 | But that gets hard to read. So we’ve split it up, three lines for three | |
261 | method calls. We already talked about `read_line()`, but what about `ok()` | |
262 | and `expect()`? Well, we already mentioned that `read_line()` puts what | |
263 | the user types into the `&mut String` we pass it. But it also returns | |
264 | a value: in this case, an [`io::Result`][ioresult]. Rust has a number of | |
265 | types named `Result` in its standard library: a generic [`Result`][result], | |
266 | and then specific versions for sub-libraries, like `io::Result`. | |
267 | ||
268 | [ioresult]: ../std/io/type.Result.html | |
269 | [result]: ../std/result/enum.Result.html | |
270 | ||
271 | The purpose of these `Result` types is to encode error handling information. | |
272 | Values of the `Result` type, like any type, have methods defined on them. In | |
273 | this case, `io::Result` has an `ok()` method, which says ‘we want to assume | |
274 | this value is a successful one. If not, just throw away the error | |
275 | information’. Why throw it away? Well, for a basic program, we just want to | |
276 | print a generic error, as basically any issue means we can’t continue. The | |
277 | [`ok()` method][ok] returns a value which has another method defined on it: | |
278 | `expect()`. The [`expect()` method][expect] takes a value it’s called on, and | |
279 | if it isn’t a successful one, [`panic!`][panic]s with a message you | |
280 | passed it. A `panic!` like this will cause our program to crash, displaying | |
281 | the message. | |
282 | ||
283 | [ok]: ../std/result/enum.Result.html#method.ok | |
284 | [expect]: ../std/option/enum.Option.html#method.expect | |
285 | [panic]: error-handling.html | |
286 | ||
287 | If we leave off calling these two methods, our program will compile, but | |
288 | we’ll get a warning: | |
289 | ||
290 | ```bash | |
291 | $ cargo build | |
292 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) | |
293 | src/main.rs:10:5: 10:39 warning: unused result which must be used, | |
294 | #[warn(unused_must_use)] on by default | |
295 | src/main.rs:10 io::stdin().read_line(&mut guess); | |
296 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
297 | ``` | |
298 | ||
299 | Rust warns us that we haven’t used the `Result` value. This warning comes from | |
300 | a special annotation that `io::Result` has. Rust is trying to tell you that | |
301 | you haven’t handled a possible error. The right way to suppress the error is | |
302 | to actually write error handling. Luckily, if we just want to crash if there’s | |
303 | a problem, we can use these two little methods. If we can recover from the | |
304 | error somehow, we’d do something else, but we’ll save that for a future | |
305 | project. | |
306 | ||
307 | There’s just one line of this first example left: | |
308 | ||
309 | ```rust,ignore | |
310 | println!("You guessed: {}", guess); | |
311 | } | |
312 | ``` | |
313 | ||
314 | This prints out the string we saved our input in. The `{}`s are a placeholder, | |
315 | and so we pass it `guess` as an argument. If we had multiple `{}`s, we would | |
316 | pass multiple arguments: | |
317 | ||
318 | ```rust | |
319 | let x = 5; | |
320 | let y = 10; | |
321 | ||
322 | println!("x and y: {} and {}", x, y); | |
323 | ``` | |
324 | ||
325 | Easy. | |
326 | ||
327 | Anyway, that’s the tour. We can run what we have with `cargo run`: | |
328 | ||
329 | ```bash | |
330 | $ cargo run | |
331 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) | |
332 | Running `target/debug/guessing_game` | |
333 | Guess the number! | |
334 | Please input your guess. | |
335 | 6 | |
336 | You guessed: 6 | |
337 | ``` | |
338 | ||
339 | All right! Our first part is done: we can get input from the keyboard, | |
340 | and then print it back out. | |
341 | ||
342 | # Generating a secret number | |
343 | ||
344 | Next, we need to generate a secret number. Rust does not yet include random | |
345 | number functionality in its standard library. The Rust team does, however, | |
346 | provide a [`rand` crate][randcrate]. A ‘crate’ is a package of Rust code. | |
347 | We’ve been building a ‘binary crate’, which is an executable. `rand` is a | |
348 | ‘library crate’, which contains code that’s intended to be used with other | |
349 | programs. | |
350 | ||
351 | [randcrate]: https://crates.io/crates/rand | |
352 | ||
353 | Using external crates is where Cargo really shines. Before we can write | |
354 | the code using `rand`, we need to modify our `Cargo.toml`. Open it up, and | |
355 | add these few lines at the bottom: | |
356 | ||
357 | ```toml | |
358 | [dependencies] | |
359 | ||
360 | rand="0.3.0" | |
361 | ``` | |
362 | ||
363 | The `[dependencies]` section of `Cargo.toml` is like the `[package]` section: | |
364 | everything that follows it is part of it, until the next section starts. | |
365 | Cargo uses the dependencies section to know what dependencies on external | |
c1a9b12d SL |
366 | crates you have, and what versions you require. In this case, we’ve specified version `0.3.0`, |
367 | which Cargo understands to be any release that’s compatible with this specific version. | |
bd371182 | 368 | Cargo understands [Semantic Versioning][semver], which is a standard for writing version |
c1a9b12d SL |
369 | numbers. If we wanted to use only `0.3.0` exactly, we could use `=0.3.0`. If we |
370 | wanted to use the latest version we could use `*`; We could use a range of | |
371 | versions. [Cargo’s documentation][cargodoc] contains more details. | |
bd371182 AL |
372 | |
373 | [semver]: http://semver.org | |
374 | [cargodoc]: http://doc.crates.io/crates-io.html | |
375 | ||
376 | Now, without changing any of our code, let’s build our project: | |
377 | ||
378 | ```bash | |
379 | $ cargo build | |
380 | Updating registry `https://github.com/rust-lang/crates.io-index` | |
381 | Downloading rand v0.3.8 | |
382 | Downloading libc v0.1.6 | |
383 | Compiling libc v0.1.6 | |
384 | Compiling rand v0.3.8 | |
385 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) | |
386 | ``` | |
387 | ||
388 | (You may see different versions, of course.) | |
389 | ||
390 | Lots of new output! Now that we have an external dependency, Cargo fetches the | |
391 | latest versions of everything from the registry, which is a copy of data from | |
392 | [Crates.io][cratesio]. Crates.io is where people in the Rust ecosystem | |
393 | post their open source Rust projects for others to use. | |
394 | ||
395 | [cratesio]: https://crates.io | |
396 | ||
397 | After updating the registry, Cargo checks our `[dependencies]` and downloads | |
398 | any we don’t have yet. In this case, while we only said we wanted to depend on | |
399 | `rand`, we’ve also grabbed a copy of `libc`. This is because `rand` depends on | |
400 | `libc` to work. After downloading them, it compiles them, and then compiles | |
401 | our project. | |
402 | ||
403 | If we run `cargo build` again, we’ll get different output: | |
404 | ||
405 | ```bash | |
406 | $ cargo build | |
407 | ``` | |
408 | ||
409 | That’s right, no output! Cargo knows that our project has been built, and that | |
410 | all of its dependencies are built, and so there’s no reason to do all that | |
411 | stuff. With nothing to do, it simply exits. If we open up `src/main.rs` again, | |
412 | make a trivial change, and then save it again, we’ll just see one line: | |
413 | ||
414 | ```bash | |
415 | $ cargo build | |
416 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) | |
417 | ``` | |
418 | ||
419 | So, we told Cargo we wanted any `0.3.x` version of `rand`, and so it fetched the latest | |
420 | version at the time this was written, `v0.3.8`. But what happens when next | |
421 | week, version `v0.3.9` comes out, with an important bugfix? While getting | |
422 | bugfixes is important, what if `0.3.9` contains a regression that breaks our | |
423 | code? | |
424 | ||
425 | The answer to this problem is the `Cargo.lock` file you’ll now find in your | |
426 | project directory. When you build your project for the first time, Cargo | |
427 | figures out all of the versions that fit your criteria, and then writes them | |
428 | to the `Cargo.lock` file. When you build your project in the future, Cargo | |
429 | will see that the `Cargo.lock` file exists, and then use that specific version | |
430 | rather than do all the work of figuring out versions again. This lets you | |
431 | have a repeatable build automatically. In other words, we’ll stay at `0.3.8` | |
432 | until we explicitly upgrade, and so will anyone who we share our code with, | |
433 | thanks to the lock file. | |
434 | ||
435 | What about when we _do_ want to use `v0.3.9`? Cargo has another command, | |
436 | `update`, which says ‘ignore the lock, figure out all the latest versions that | |
437 | fit what we’ve specified. If that works, write those versions out to the lock | |
438 | file’. But, by default, Cargo will only look for versions larger than `0.3.0` | |
439 | and smaller than `0.4.0`. If we want to move to `0.4.x`, we’d have to update | |
440 | the `Cargo.toml` directly. When we do, the next time we `cargo build`, Cargo | |
441 | will update the index and re-evaluate our `rand` requirements. | |
442 | ||
443 | There’s a lot more to say about [Cargo][doccargo] and [its | |
444 | ecosystem][doccratesio], but for now, that’s all we need to know. Cargo makes | |
445 | it really easy to re-use libraries, and so Rustaceans tend to write smaller | |
446 | projects which are assembled out of a number of sub-packages. | |
447 | ||
448 | [doccargo]: http://doc.crates.io | |
449 | [doccratesio]: http://doc.crates.io/crates-io.html | |
450 | ||
451 | Let’s get on to actually _using_ `rand`. Here’s our next step: | |
452 | ||
453 | ```rust,ignore | |
454 | extern crate rand; | |
455 | ||
456 | use std::io; | |
457 | use rand::Rng; | |
458 | ||
459 | fn main() { | |
460 | println!("Guess the number!"); | |
461 | ||
462 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
463 | ||
464 | println!("The secret number is: {}", secret_number); | |
465 | ||
466 | println!("Please input your guess."); | |
467 | ||
468 | let mut guess = String::new(); | |
469 | ||
470 | io::stdin().read_line(&mut guess) | |
471 | .ok() | |
472 | .expect("failed to read line"); | |
473 | ||
474 | println!("You guessed: {}", guess); | |
475 | } | |
476 | ``` | |
477 | ||
478 | The first thing we’ve done is change the first line. It now says | |
479 | `extern crate rand`. Because we declared `rand` in our `[dependencies]`, we | |
480 | can use `extern crate` to let Rust know we’ll be making use of it. This also | |
481 | does the equivalent of a `use rand;` as well, so we can make use of anything | |
482 | in the `rand` crate by prefixing it with `rand::`. | |
483 | ||
484 | Next, we added another `use` line: `use rand::Rng`. We’re going to use a | |
485 | method in a moment, and it requires that `Rng` be in scope to work. The basic | |
486 | idea is this: methods are defined on something called ‘traits’, and for the | |
487 | method to work, it needs the trait to be in scope. For more about the | |
488 | details, read the [traits][traits] section. | |
489 | ||
490 | [traits]: traits.html | |
491 | ||
492 | There are two other lines we added, in the middle: | |
493 | ||
494 | ```rust,ignore | |
495 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
496 | ||
497 | println!("The secret number is: {}", secret_number); | |
498 | ``` | |
499 | ||
500 | We use the `rand::thread_rng()` function to get a copy of the random number | |
501 | generator, which is local to the particular [thread][concurrency] of execution | |
502 | we’re in. Because we `use rand::Rng`’d above, it has a `gen_range()` method | |
503 | available. This method takes two arguments, and generates a number between | |
504 | them. It’s inclusive on the lower bound, but exclusive on the upper bound, | |
c1a9b12d | 505 | so we need `1` and `101` to get a number ranging from one to a hundred. |
bd371182 AL |
506 | |
507 | [concurrency]: concurrency.html | |
508 | ||
509 | The second line just prints out the secret number. This is useful while | |
510 | we’re developing our program, so we can easily test it out. But we’ll be | |
511 | deleting it for the final version. It’s not much of a game if it prints out | |
512 | the answer when you start it up! | |
513 | ||
514 | Try running our new program a few times: | |
515 | ||
516 | ```bash | |
517 | $ cargo run | |
518 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) | |
519 | Running `target/debug/guessing_game` | |
520 | Guess the number! | |
521 | The secret number is: 7 | |
522 | Please input your guess. | |
523 | 4 | |
524 | You guessed: 4 | |
525 | $ cargo run | |
526 | Running `target/debug/guessing_game` | |
527 | Guess the number! | |
528 | The secret number is: 83 | |
529 | Please input your guess. | |
530 | 5 | |
531 | You guessed: 5 | |
532 | ``` | |
533 | ||
534 | Great! Next up: let’s compare our guess to the secret guess. | |
535 | ||
536 | # Comparing guesses | |
537 | ||
538 | Now that we’ve got user input, let’s compare our guess to the random guess. | |
e9174d1e | 539 | Here’s our next step, though it doesn’t quite compile yet: |
bd371182 AL |
540 | |
541 | ```rust,ignore | |
542 | extern crate rand; | |
543 | ||
544 | use std::io; | |
545 | use std::cmp::Ordering; | |
546 | use rand::Rng; | |
547 | ||
548 | fn main() { | |
549 | println!("Guess the number!"); | |
550 | ||
551 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
552 | ||
553 | println!("The secret number is: {}", secret_number); | |
554 | ||
555 | println!("Please input your guess."); | |
556 | ||
557 | let mut guess = String::new(); | |
558 | ||
559 | io::stdin().read_line(&mut guess) | |
560 | .ok() | |
561 | .expect("failed to read line"); | |
562 | ||
563 | println!("You guessed: {}", guess); | |
564 | ||
565 | match guess.cmp(&secret_number) { | |
566 | Ordering::Less => println!("Too small!"), | |
567 | Ordering::Greater => println!("Too big!"), | |
568 | Ordering::Equal => println!("You win!"), | |
569 | } | |
570 | } | |
571 | ``` | |
572 | ||
573 | A few new bits here. The first is another `use`. We bring a type called | |
574 | `std::cmp::Ordering` into scope. Then, five new lines at the bottom that use | |
575 | it: | |
576 | ||
577 | ```rust,ignore | |
578 | match guess.cmp(&secret_number) { | |
579 | Ordering::Less => println!("Too small!"), | |
580 | Ordering::Greater => println!("Too big!"), | |
581 | Ordering::Equal => println!("You win!"), | |
582 | } | |
583 | ``` | |
584 | ||
585 | The `cmp()` method can be called on anything that can be compared, and it | |
586 | takes a reference to the thing you want to compare it to. It returns the | |
587 | `Ordering` type we `use`d earlier. We use a [`match`][match] statement to | |
588 | determine exactly what kind of `Ordering` it is. `Ordering` is an | |
589 | [`enum`][enum], short for ‘enumeration’, which looks like this: | |
590 | ||
591 | ```rust | |
592 | enum Foo { | |
593 | Bar, | |
594 | Baz, | |
595 | } | |
596 | ``` | |
597 | ||
598 | [match]: match.html | |
599 | [enum]: enums.html | |
600 | ||
601 | With this definition, anything of type `Foo` can be either a | |
602 | `Foo::Bar` or a `Foo::Baz`. We use the `::` to indicate the | |
603 | namespace for a particular `enum` variant. | |
604 | ||
b039eaaf | 605 | The [`Ordering`][ordering] `enum` has three possible variants: `Less`, `Equal`, |
bd371182 AL |
606 | and `Greater`. The `match` statement takes a value of a type, and lets you |
607 | create an ‘arm’ for each possible value. Since we have three types of | |
608 | `Ordering`, we have three arms: | |
609 | ||
610 | ```rust,ignore | |
611 | match guess.cmp(&secret_number) { | |
612 | Ordering::Less => println!("Too small!"), | |
613 | Ordering::Greater => println!("Too big!"), | |
614 | Ordering::Equal => println!("You win!"), | |
615 | } | |
616 | ``` | |
617 | ||
618 | [ordering]: ../std/cmp/enum.Ordering.html | |
619 | ||
620 | If it’s `Less`, we print `Too small!`, if it’s `Greater`, `Too big!`, and if | |
621 | `Equal`, `You win!`. `match` is really useful, and is used often in Rust. | |
622 | ||
e9174d1e | 623 | I did mention that this won’t quite compile yet, though. Let’s try it: |
bd371182 AL |
624 | |
625 | ```bash | |
626 | $ cargo build | |
627 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) | |
628 | src/main.rs:28:21: 28:35 error: mismatched types: | |
629 | expected `&collections::string::String`, | |
630 | found `&_` | |
631 | (expected struct `collections::string::String`, | |
632 | found integral variable) [E0308] | |
633 | src/main.rs:28 match guess.cmp(&secret_number) { | |
634 | ^~~~~~~~~~~~~~ | |
635 | error: aborting due to previous error | |
636 | Could not compile `guessing_game`. | |
637 | ``` | |
638 | ||
639 | Whew! This is a big error. The core of it is that we have ‘mismatched types’. | |
640 | Rust has a strong, static type system. However, it also has type inference. | |
641 | When we wrote `let guess = String::new()`, Rust was able to infer that `guess` | |
642 | should be a `String`, and so it doesn’t make us write out the type. And with | |
643 | our `secret_number`, there are a number of types which can have a value | |
644 | between one and a hundred: `i32`, a thirty-two-bit number, or `u32`, an | |
62682a34 | 645 | unsigned thirty-two-bit number, or `i64`, a sixty-four-bit number or others. |
bd371182 AL |
646 | So far, that hasn’t mattered, and so Rust defaults to an `i32`. However, here, |
647 | Rust doesn’t know how to compare the `guess` and the `secret_number`. They | |
648 | need to be the same type. Ultimately, we want to convert the `String` we | |
649 | read as input into a real number type, for comparison. We can do that | |
650 | with three more lines. Here’s our new program: | |
651 | ||
652 | ```rust,ignore | |
653 | extern crate rand; | |
654 | ||
655 | use std::io; | |
656 | use std::cmp::Ordering; | |
657 | use rand::Rng; | |
658 | ||
659 | fn main() { | |
660 | println!("Guess the number!"); | |
661 | ||
662 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
663 | ||
664 | println!("The secret number is: {}", secret_number); | |
665 | ||
666 | println!("Please input your guess."); | |
667 | ||
668 | let mut guess = String::new(); | |
669 | ||
670 | io::stdin().read_line(&mut guess) | |
671 | .ok() | |
672 | .expect("failed to read line"); | |
673 | ||
674 | let guess: u32 = guess.trim().parse() | |
675 | .ok() | |
676 | .expect("Please type a number!"); | |
677 | ||
678 | println!("You guessed: {}", guess); | |
679 | ||
680 | match guess.cmp(&secret_number) { | |
681 | Ordering::Less => println!("Too small!"), | |
682 | Ordering::Greater => println!("Too big!"), | |
683 | Ordering::Equal => println!("You win!"), | |
684 | } | |
685 | } | |
686 | ``` | |
687 | ||
688 | The new three lines: | |
689 | ||
690 | ```rust,ignore | |
691 | let guess: u32 = guess.trim().parse() | |
692 | .ok() | |
693 | .expect("Please type a number!"); | |
694 | ``` | |
695 | ||
696 | Wait a minute, I thought we already had a `guess`? We do, but Rust allows us | |
697 | to ‘shadow’ the previous `guess` with a new one. This is often used in this | |
698 | exact situation, where `guess` starts as a `String`, but we want to convert it | |
699 | to an `u32`. Shadowing lets us re-use the `guess` name, rather than forcing us | |
700 | to come up with two unique names like `guess_str` and `guess`, or something | |
701 | else. | |
702 | ||
703 | We bind `guess` to an expression that looks like something we wrote earlier: | |
704 | ||
705 | ```rust,ignore | |
706 | guess.trim().parse() | |
707 | ``` | |
708 | ||
709 | Followed by an `ok().expect()` invocation. Here, `guess` refers to the old | |
710 | `guess`, the one that was a `String` with our input in it. The `trim()` | |
711 | method on `String`s will eliminate any white space at the beginning and end of | |
712 | our string. This is important, as we had to press the ‘return’ key to satisfy | |
713 | `read_line()`. This means that if we type `5` and hit return, `guess` looks | |
714 | like this: `5\n`. The `\n` represents ‘newline’, the enter key. `trim()` gets | |
715 | rid of this, leaving our string with just the `5`. The [`parse()` method on | |
716 | strings][parse] parses a string into some kind of number. Since it can parse a | |
717 | variety of numbers, we need to give Rust a hint as to the exact type of number | |
718 | we want. Hence, `let guess: u32`. The colon (`:`) after `guess` tells Rust | |
719 | we’re going to annotate its type. `u32` is an unsigned, thirty-two bit | |
720 | integer. Rust has [a number of built-in number types][number], but we’ve | |
721 | chosen `u32`. It’s a good default choice for a small positive number. | |
722 | ||
723 | [parse]: ../std/primitive.str.html#method.parse | |
724 | [number]: primitive-types.html#numeric-types | |
725 | ||
726 | Just like `read_line()`, our call to `parse()` could cause an error. What if | |
727 | our string contained `A👍%`? There’d be no way to convert that to a number. As | |
728 | such, we’ll do the same thing we did with `read_line()`: use the `ok()` and | |
729 | `expect()` methods to crash if there’s an error. | |
730 | ||
731 | Let’s try our program out! | |
732 | ||
733 | ```bash | |
734 | $ cargo run | |
d9579d0f | 735 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) |
bd371182 AL |
736 | Running `target/guessing_game` |
737 | Guess the number! | |
738 | The secret number is: 58 | |
739 | Please input your guess. | |
740 | 76 | |
741 | You guessed: 76 | |
742 | Too big! | |
743 | ``` | |
744 | ||
745 | Nice! You can see I even added spaces before my guess, and it still figured | |
746 | out that I guessed 76. Run the program a few times, and verify that guessing | |
747 | the number works, as well as guessing a number too small. | |
748 | ||
749 | Now we’ve got most of the game working, but we can only make one guess. Let’s | |
750 | change that by adding loops! | |
751 | ||
752 | # Looping | |
753 | ||
754 | The `loop` keyword gives us an infinite loop. Let’s add that in: | |
755 | ||
756 | ```rust,ignore | |
757 | extern crate rand; | |
758 | ||
759 | use std::io; | |
760 | use std::cmp::Ordering; | |
761 | use rand::Rng; | |
762 | ||
763 | fn main() { | |
764 | println!("Guess the number!"); | |
765 | ||
766 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
767 | ||
768 | println!("The secret number is: {}", secret_number); | |
769 | ||
770 | loop { | |
771 | println!("Please input your guess."); | |
772 | ||
773 | let mut guess = String::new(); | |
774 | ||
775 | io::stdin().read_line(&mut guess) | |
776 | .ok() | |
777 | .expect("failed to read line"); | |
778 | ||
779 | let guess: u32 = guess.trim().parse() | |
780 | .ok() | |
781 | .expect("Please type a number!"); | |
782 | ||
783 | println!("You guessed: {}", guess); | |
784 | ||
785 | match guess.cmp(&secret_number) { | |
786 | Ordering::Less => println!("Too small!"), | |
787 | Ordering::Greater => println!("Too big!"), | |
788 | Ordering::Equal => println!("You win!"), | |
789 | } | |
790 | } | |
791 | } | |
792 | ``` | |
793 | ||
794 | And try it out. But wait, didn’t we just add an infinite loop? Yup. Remember | |
795 | our discussion about `parse()`? If we give a non-number answer, we’ll `return` | |
796 | and quit. Observe: | |
797 | ||
798 | ```bash | |
799 | $ cargo run | |
d9579d0f | 800 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) |
bd371182 AL |
801 | Running `target/guessing_game` |
802 | Guess the number! | |
803 | The secret number is: 59 | |
804 | Please input your guess. | |
805 | 45 | |
806 | You guessed: 45 | |
807 | Too small! | |
808 | Please input your guess. | |
809 | 60 | |
810 | You guessed: 60 | |
811 | Too big! | |
812 | Please input your guess. | |
813 | 59 | |
814 | You guessed: 59 | |
815 | You win! | |
816 | Please input your guess. | |
817 | quit | |
818 | thread '<main>' panicked at 'Please type a number!' | |
819 | ``` | |
820 | ||
821 | Ha! `quit` actually quits. As does any other non-number input. Well, this is | |
822 | suboptimal to say the least. First, let’s actually quit when you win the game: | |
823 | ||
824 | ```rust,ignore | |
825 | extern crate rand; | |
826 | ||
827 | use std::io; | |
828 | use std::cmp::Ordering; | |
829 | use rand::Rng; | |
830 | ||
831 | fn main() { | |
832 | println!("Guess the number!"); | |
833 | ||
834 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
835 | ||
836 | println!("The secret number is: {}", secret_number); | |
837 | ||
838 | loop { | |
839 | println!("Please input your guess."); | |
840 | ||
841 | let mut guess = String::new(); | |
842 | ||
843 | io::stdin().read_line(&mut guess) | |
844 | .ok() | |
845 | .expect("failed to read line"); | |
846 | ||
847 | let guess: u32 = guess.trim().parse() | |
848 | .ok() | |
849 | .expect("Please type a number!"); | |
850 | ||
851 | println!("You guessed: {}", guess); | |
852 | ||
853 | match guess.cmp(&secret_number) { | |
854 | Ordering::Less => println!("Too small!"), | |
855 | Ordering::Greater => println!("Too big!"), | |
856 | Ordering::Equal => { | |
857 | println!("You win!"); | |
858 | break; | |
859 | } | |
860 | } | |
861 | } | |
862 | } | |
863 | ``` | |
864 | ||
865 | By adding the `break` line after the `You win!`, we’ll exit the loop when we | |
866 | win. Exiting the loop also means exiting the program, since it’s the last | |
867 | thing in `main()`. We have just one more tweak to make: when someone inputs a | |
868 | non-number, we don’t want to quit, we just want to ignore it. We can do that | |
869 | like this: | |
870 | ||
871 | ```rust,ignore | |
872 | extern crate rand; | |
873 | ||
874 | use std::io; | |
875 | use std::cmp::Ordering; | |
876 | use rand::Rng; | |
877 | ||
878 | fn main() { | |
879 | println!("Guess the number!"); | |
880 | ||
881 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
882 | ||
883 | println!("The secret number is: {}", secret_number); | |
884 | ||
885 | loop { | |
886 | println!("Please input your guess."); | |
887 | ||
888 | let mut guess = String::new(); | |
889 | ||
890 | io::stdin().read_line(&mut guess) | |
891 | .ok() | |
892 | .expect("failed to read line"); | |
893 | ||
894 | let guess: u32 = match guess.trim().parse() { | |
895 | Ok(num) => num, | |
896 | Err(_) => continue, | |
897 | }; | |
898 | ||
899 | println!("You guessed: {}", guess); | |
900 | ||
901 | match guess.cmp(&secret_number) { | |
902 | Ordering::Less => println!("Too small!"), | |
903 | Ordering::Greater => println!("Too big!"), | |
904 | Ordering::Equal => { | |
905 | println!("You win!"); | |
906 | break; | |
907 | } | |
908 | } | |
909 | } | |
910 | } | |
911 | ``` | |
912 | ||
913 | These are the lines that changed: | |
914 | ||
915 | ```rust,ignore | |
916 | let guess: u32 = match guess.trim().parse() { | |
917 | Ok(num) => num, | |
918 | Err(_) => continue, | |
919 | }; | |
920 | ``` | |
921 | ||
922 | This is how you generally move from ‘crash on error’ to ‘actually handle the | |
923 | error’, by switching from `ok().expect()` to a `match` statement. The `Result` | |
b039eaaf | 924 | returned by `parse()` is an `enum` just like `Ordering`, but in this case, each |
bd371182 | 925 | variant has some data associated with it: `Ok` is a success, and `Err` is a |
b039eaaf | 926 | failure. Each contains more information: the successfully parsed integer, or an |
bd371182 AL |
927 | error type. In this case, we `match` on `Ok(num)`, which sets the inner value |
928 | of the `Ok` to the name `num`, and then we just return it on the right-hand | |
929 | side. In the `Err` case, we don’t care what kind of error it is, so we just | |
930 | use `_` instead of a name. This ignores the error, and `continue` causes us | |
931 | to go to the next iteration of the `loop`. | |
932 | ||
933 | Now we should be good! Let’s try: | |
934 | ||
935 | ```bash | |
936 | $ cargo run | |
d9579d0f | 937 | Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) |
bd371182 AL |
938 | Running `target/guessing_game` |
939 | Guess the number! | |
940 | The secret number is: 61 | |
941 | Please input your guess. | |
942 | 10 | |
943 | You guessed: 10 | |
944 | Too small! | |
945 | Please input your guess. | |
946 | 99 | |
947 | You guessed: 99 | |
948 | Too big! | |
949 | Please input your guess. | |
950 | foo | |
951 | Please input your guess. | |
952 | 61 | |
953 | You guessed: 61 | |
954 | You win! | |
955 | ``` | |
956 | ||
957 | Awesome! With one tiny last tweak, we have finished the guessing game. Can you | |
958 | think of what it is? That’s right, we don’t want to print out the secret | |
959 | number. It was good for testing, but it kind of ruins the game. Here’s our | |
960 | final source: | |
961 | ||
962 | ```rust,ignore | |
963 | extern crate rand; | |
964 | ||
965 | use std::io; | |
966 | use std::cmp::Ordering; | |
967 | use rand::Rng; | |
968 | ||
969 | fn main() { | |
970 | println!("Guess the number!"); | |
971 | ||
972 | let secret_number = rand::thread_rng().gen_range(1, 101); | |
973 | ||
974 | loop { | |
975 | println!("Please input your guess."); | |
976 | ||
977 | let mut guess = String::new(); | |
978 | ||
979 | io::stdin().read_line(&mut guess) | |
980 | .ok() | |
981 | .expect("failed to read line"); | |
982 | ||
983 | let guess: u32 = match guess.trim().parse() { | |
984 | Ok(num) => num, | |
985 | Err(_) => continue, | |
986 | }; | |
987 | ||
988 | println!("You guessed: {}", guess); | |
989 | ||
990 | match guess.cmp(&secret_number) { | |
991 | Ordering::Less => println!("Too small!"), | |
992 | Ordering::Greater => println!("Too big!"), | |
993 | Ordering::Equal => { | |
994 | println!("You win!"); | |
995 | break; | |
996 | } | |
997 | } | |
998 | } | |
999 | } | |
1000 | ``` | |
1001 | ||
1002 | # Complete! | |
1003 | ||
1004 | At this point, you have successfully built the Guessing Game! Congratulations! | |
1005 | ||
1006 | This first project showed you a lot: `let`, `match`, methods, associated | |
1007 | functions, using external crates, and more. Our next project will show off | |
1008 | even more. |