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