3 Okay! We've got the basics of Rust down. Let's write a bigger program.
5 For our first project, we'll implement a classic beginner programming problem:
6 the guessing game. Here's how it works: Our program will generate a random
7 integer between one and a hundred. It will then prompt us to enter a guess.
8 Upon entering our guess, it will tell us if we're too low or too high. Once we
9 guess correctly, it will congratulate us. Sound good?
13 Let's set up a new project. Go to your projects directory. Remember how we
14 had to create our directory structure and a `Cargo.toml` for `hello_world`? Cargo
15 has a command that does that for us. Let's give it a shot:
19 $ cargo new guessing_game --bin
23 We pass the name of our project to `cargo new`, and then the `--bin` flag,
24 since we're making a binary, rather than a library.
26 Check out the generated `Cargo.toml`:
31 name = "guessing_game"
33 authors = ["Your Name <you@example.com>"]
36 Cargo gets this information from your environment. If it's not correct, go ahead
39 Finally, Cargo generated a "Hello, world!" for us. Check out `src/main.rs`:
43 println!("Hello, world!")
47 Let's try compiling what Cargo gave us:
51 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
54 Excellent! Open up your `src/main.rs` again. We'll be writing all of
55 our code in this file. We'll talk about multiple-file projects later on in the
58 Before we move on, let me show you one more Cargo command: `run`. `cargo run`
59 is kind of like `cargo build`, but it also then runs the produced executable.
64 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
65 Running `target/guessing_game`
69 Great! The `run` command comes in handy when you need to rapidly iterate on a project.
70 Our game is just such a project, we need to quickly test each iteration before moving on to the next one.
74 Let's get to it! The first thing we need to do for our guessing game is
75 allow our player to input a guess. Put this in your `src/main.rs`:
81 println!("Guess the number!");
83 println!("Please input your guess.");
85 let input = old_io::stdin().read_line()
87 .expect("Failed to read line");
89 println!("You guessed: {}", input);
93 You've seen this code before, when we talked about standard input. We
94 import the `std::old_io` module with `use`, and then our `main` function contains
95 our program's logic. We print a little message announcing the game, ask the
96 user to input a guess, get their input, and then print it out.
98 Because we talked about this in the section on standard I/O, I won't go into
99 more details here. If you need a refresher, go re-read that section.
101 ## Generating a secret number
103 Next, we need to generate a secret number. To do that, we need to use Rust's
104 random number generation, which we haven't talked about yet. Rust includes a
105 bunch of interesting functions in its standard library. If you need a bit of
106 code, it's possible that it's already been written for you! In this case,
107 we do know that Rust has random number generation, but we don't know how to
110 Enter the docs. Rust has a page specifically to document the standard library.
111 You can find that page [here](../std/index.html). There's a lot of information on
112 that page, but the best part is the search bar. Right up at the top, there's
113 a box that you can enter in a search term. The search is pretty primitive
114 right now, but is getting better all the time. If you type "random" in that
115 box, the page will update to [this one](../std/index.html?search=random). The very
116 first result is a link to [`std::rand::random`](../std/rand/fn.random.html). If we
117 click on that result, we'll be taken to its documentation page.
119 This page shows us a few things: the type signature of the function, some
120 explanatory text, and then an example. Let's try to modify our code to add in the
121 `random` function and see what happens:
128 println!("Guess the number!");
130 let secret_number = (rand::random() % 100) + 1; // secret_number: i32
132 println!("The secret number is: {}", secret_number);
134 println!("Please input your guess.");
136 let input = old_io::stdin().read_line()
138 .expect("Failed to read line");
141 println!("You guessed: {}", input);
145 The first thing we changed was to `use std::rand`, as the docs
146 explained. We then added in a `let` expression to create a variable binding
147 named `secret_number`, and we printed out its result.
149 Also, you may wonder why we are using `%` on the result of `rand::random()`.
150 This operator is called *modulo*, and it returns the remainder of a division.
151 By taking the modulo of the result of `rand::random()`, we're limiting the
152 values to be between 0 and 99. Then, we add one to the result, making it from 1
153 to 100. Using modulo can give you a very, very small bias in the result, but
154 for this example, it is not important.
156 Let's try to compile this using `cargo build`:
160 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
161 src/main.rs:7:26: 7:34 error: the type of this value must be known in this context
162 src/main.rs:7 let secret_number = (rand::random() % 100) + 1;
164 error: aborting due to previous error
167 It didn't work! Rust says "the type of this value must be known in this
168 context." What's up with that? Well, as it turns out, `rand::random()` can
169 generate many kinds of random values, not just integers. And in this case, Rust
170 isn't sure what kind of value `random()` should generate. So we have to help
171 it. With number literals, we can just add an `i32` onto the end to tell Rust they're
172 integers, but that does not work with functions. There's a different syntax,
173 and it looks like this:
176 rand::random::<i32>();
179 This says "please give me a random `i32` value." We can change our code to use
187 println!("Guess the number!");
189 let secret_number = (rand::random::<i32>() % 100) + 1;
191 println!("The secret number is: {}", secret_number);
193 println!("Please input your guess.");
195 let input = old_io::stdin().read_line()
197 .expect("Failed to read line");
200 println!("You guessed: {}", input);
204 Try running our new program a few times:
208 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
209 Running `target/guessing_game`
211 The secret number is: 7
212 Please input your guess.
215 $ ./target/guessing_game
217 The secret number is: 83
218 Please input your guess.
221 $ ./target/guessing_game
223 The secret number is: -29
224 Please input your guess.
229 Wait. Negative 29? We wanted a number between one and a hundred! We have two
230 options here: we can either ask `random()` to generate an unsigned integer, which
231 can only be positive, or we can use the `abs()` function. Let's go with the
232 unsigned integer approach. If we want a random positive number, we should ask for
233 a random positive number. Our code looks like this now:
240 println!("Guess the number!");
242 let secret_number = (rand::random::<u32>() % 100) + 1;
244 println!("The secret number is: {}", secret_number);
246 println!("Please input your guess.");
248 let input = old_io::stdin().read_line()
250 .expect("Failed to read line");
253 println!("You guessed: {}", input);
261 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
262 Running `target/guessing_game`
264 The secret number is: 57
265 Please input your guess.
270 Great! Next up: let's compare our guess to the secret guess.
274 If you remember, earlier in the guide, we made a `cmp` function that compared
275 two numbers. Let's add that in, along with a `match` statement to compare our
276 guess to the secret number:
281 use std::cmp::Ordering;
284 println!("Guess the number!");
286 let secret_number = (rand::random::<u32>() % 100) + 1;
288 println!("The secret number is: {}", secret_number);
290 println!("Please input your guess.");
292 let input = old_io::stdin().read_line()
294 .expect("Failed to read line");
297 println!("You guessed: {}", input);
299 match cmp(input, secret_number) {
300 Ordering::Less => println!("Too small!"),
301 Ordering::Greater => println!("Too big!"),
302 Ordering::Equal => println!("You win!"),
306 fn cmp(a: i32, b: i32) -> Ordering {
307 if a < b { Ordering::Less }
308 else if a > b { Ordering::Greater }
309 else { Ordering::Equal }
313 If we try to compile, we'll get some errors:
317 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
318 src/main.rs:20:15: 20:20 error: mismatched types: expected `i32` but found `collections::string::String` (expected i32 but found struct collections::string::String)
319 src/main.rs:20 match cmp(input, secret_number) {
321 src/main.rs:20:22: 20:35 error: mismatched types: expected `i32` but found `u32` (expected i32 but found u32)
322 src/main.rs:20 match cmp(input, secret_number) {
324 error: aborting due to 2 previous errors
327 This often happens when writing Rust programs, and is one of Rust's greatest
328 strengths. You try out some code, see if it compiles, and Rust tells you that
329 you've done something wrong. In this case, our `cmp` function works on integers,
330 but we've given it unsigned integers. In this case, the fix is easy, because
331 we wrote the `cmp` function! Let's change it to take `u32`s:
336 use std::cmp::Ordering;
339 println!("Guess the number!");
341 let secret_number = (rand::random::<u32>() % 100) + 1;
343 println!("The secret number is: {}", secret_number);
345 println!("Please input your guess.");
347 let input = old_io::stdin().read_line()
349 .expect("Failed to read line");
352 println!("You guessed: {}", input);
354 match cmp(input, secret_number) {
355 Ordering::Less => println!("Too small!"),
356 Ordering::Greater => println!("Too big!"),
357 Ordering::Equal => println!("You win!"),
361 fn cmp(a: u32, b: u32) -> Ordering {
362 if a < b { Ordering::Less }
363 else if a > b { Ordering::Greater }
364 else { Ordering::Equal }
368 And try compiling again:
372 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
373 src/main.rs:20:15: 20:20 error: mismatched types: expected `u32` but found `collections::string::String` (expected u32 but found struct collections::string::String)
374 src/main.rs:20 match cmp(input, secret_number) {
376 error: aborting due to previous error
379 This error is similar to the last one: we expected to get a `u32`, but we got
380 a `String` instead! That's because our `input` variable is coming from the
381 standard input, and you can guess anything. Try it:
384 $ ./target/guessing_game
386 The secret number is: 73
387 Please input your guess.
392 Oops! Also, you'll note that we just ran our program even though it didn't compile.
393 This works because the older version we did successfully compile was still lying
394 around. Gotta be careful!
396 Anyway, we have a `String`, but we need a `u32`. What to do? Well, there's
400 let input = old_io::stdin().read_line()
402 .expect("Failed to read line");
403 let input_num: Result<u32, _> = input.parse();
406 The `parse` function takes in a `&str` value and converts it into something.
407 We tell it what kind of something with a type hint. Remember our type hint with
408 `random()`? It looked like this:
411 rand::random::<u32>();
414 There's an alternate way of providing a hint too, and that's declaring the type
418 let x: u32 = rand::random();
421 In this case, we say `x` is a `u32` explicitly, so Rust is able to properly
422 tell `random()` what to generate. In a similar fashion, both of these work:
425 let input_num = "5".parse::<u32>(); // input_num: Option<u32>
426 let input_num: Result<u32, _> = "5".parse(); // input_num: Result<u32, <u32 as FromStr>::Err>
429 Here we're converting the `Result` returned by `parse` to an `Option` by using
430 the `ok` method as well. Anyway, with us now converting our input to a number,
431 our code looks like this:
436 use std::cmp::Ordering;
439 println!("Guess the number!");
441 let secret_number = (rand::random::<u32>() % 100) + 1;
443 println!("The secret number is: {}", secret_number);
445 println!("Please input your guess.");
447 let input = old_io::stdin().read_line()
449 .expect("Failed to read line");
450 let input_num: Result<u32, _> = input.parse();
452 println!("You guessed: {:?}", input_num);
454 match cmp(input_num, secret_number) {
455 Ordering::Less => println!("Too small!"),
456 Ordering::Greater => println!("Too big!"),
457 Ordering::Equal => println!("You win!"),
461 fn cmp(a: u32, b: u32) -> Ordering {
462 if a < b { Ordering::Less }
463 else if a > b { Ordering::Greater }
464 else { Ordering::Equal }
472 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
473 src/main.rs:22:15: 22:24 error: mismatched types: expected `u32` but found `core::option::Option<u32>` (expected u32 but found enum core::option::Option)
474 src/main.rs:22 match cmp(input_num, secret_number) {
476 error: aborting due to previous error
479 Oh yeah! Our `input_num` has the type `Option<u32>`, rather than `u32`. We
480 need to unwrap the Option. If you remember from before, `match` is a great way
481 to do that. Try this code:
486 use std::cmp::Ordering;
489 println!("Guess the number!");
491 let secret_number = (rand::random::<u32>() % 100) + 1;
493 println!("The secret number is: {}", secret_number);
495 println!("Please input your guess.");
497 let input = old_io::stdin().read_line()
499 .expect("Failed to read line");
500 let input_num: Result<u32, _> = input.parse();
502 let num = match input_num {
505 println!("Please input a number!");
511 println!("You guessed: {}", num);
513 match cmp(num, secret_number) {
514 Ordering::Less => println!("Too small!"),
515 Ordering::Greater => println!("Too big!"),
516 Ordering::Equal => println!("You win!"),
520 fn cmp(a: u32, b: u32) -> Ordering {
521 if a < b { Ordering::Less }
522 else if a > b { Ordering::Greater }
523 else { Ordering::Equal }
527 We use a `match` to either give us the `u32` inside of the `Option`, or else
528 print an error message and return. Let's give this a shot:
532 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
533 Running `target/guessing_game`
535 The secret number is: 17
536 Please input your guess.
538 Please input a number!
541 Uh, what? But we did!
543 ... actually, we didn't. See, when you get a line of input from `stdin()`,
544 you get all the input. Including the `\n` character from you pressing Enter.
545 Therefore, `parse()` sees the string `"5\n"` and says "nope, that's not a
546 number; there's non-number stuff in there!" Luckily for us, `&str`s have an easy
547 method we can use defined on them: `trim()`. One small modification, and our
548 code looks like this:
553 use std::cmp::Ordering;
556 println!("Guess the number!");
558 let secret_number = (rand::random::<u32>() % 100) + 1;
560 println!("The secret number is: {}", secret_number);
562 println!("Please input your guess.");
564 let input = old_io::stdin().read_line()
566 .expect("Failed to read line");
567 let input_num: Result<u32, _> = input.trim().parse();
569 let num = match input_num {
572 println!("Please input a number!");
578 println!("You guessed: {}", num);
580 match cmp(num, secret_number) {
581 Ordering::Less => println!("Too small!"),
582 Ordering::Greater => println!("Too big!"),
583 Ordering::Equal => println!("You win!"),
587 fn cmp(a: u32, b: u32) -> Ordering {
588 if a < b { Ordering::Less }
589 else if a > b { Ordering::Greater }
590 else { Ordering::Equal }
598 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
599 Running `target/guessing_game`
601 The secret number is: 58
602 Please input your guess.
608 Nice! You can see I even added spaces before my guess, and it still figured
609 out that I guessed 76. Run the program a few times, and verify that guessing
610 the number works, as well as guessing a number too small.
612 The Rust compiler helped us out quite a bit there! This technique is called
613 "leaning on the compiler", and it's often useful when working on some code.
614 Let the error messages help guide you towards the correct types.
616 Now we've got most of the game working, but we can only make one guess. Let's
617 change that by adding loops!
621 As we already discussed, the `loop` keyword gives us an infinite loop.
627 use std::cmp::Ordering;
630 println!("Guess the number!");
632 let secret_number = (rand::random::<u32>() % 100) + 1;
634 println!("The secret number is: {}", secret_number);
638 println!("Please input your guess.");
640 let input = old_io::stdin().read_line()
642 .expect("Failed to read line");
643 let input_num: Result<u32, _> = input.trim().parse();
645 let num = match input_num {
648 println!("Please input a number!");
654 println!("You guessed: {}", num);
656 match cmp(num, secret_number) {
657 Ordering::Less => println!("Too small!"),
658 Ordering::Greater => println!("Too big!"),
659 Ordering::Equal => println!("You win!"),
664 fn cmp(a: u32, b: u32) -> Ordering {
665 if a < b { Ordering::Less }
666 else if a > b { Ordering::Greater }
667 else { Ordering::Equal }
671 And try it out. But wait, didn't we just add an infinite loop? Yup. Remember
672 that `return`? If we give a non-number answer, we'll `return` and quit. Observe:
676 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
677 Running `target/guessing_game`
679 The secret number is: 59
680 Please input your guess.
684 Please input your guess.
688 Please input your guess.
692 Please input your guess.
694 Please input a number!
697 Ha! `quit` actually quits. As does any other non-number input. Well, this is
698 suboptimal to say the least. First, let's actually quit when you win the game:
703 use std::cmp::Ordering;
706 println!("Guess the number!");
708 let secret_number = (rand::random::<u32>() % 100) + 1;
710 println!("The secret number is: {}", secret_number);
714 println!("Please input your guess.");
716 let input = old_io::stdin().read_line()
718 .expect("Failed to read line");
719 let input_num: Result<u32, _> = input.trim().parse();
721 let num = match input_num {
724 println!("Please input a number!");
730 println!("You guessed: {}", num);
732 match cmp(num, secret_number) {
733 Ordering::Less => println!("Too small!"),
734 Ordering::Greater => println!("Too big!"),
736 println!("You win!");
743 fn cmp(a: u32, b: u32) -> Ordering {
744 if a < b { Ordering::Less }
745 else if a > b { Ordering::Greater }
746 else { Ordering::Equal }
750 By adding the `return` line after the `You win!`, we'll exit the program when
751 we win. We have just one more tweak to make: when someone inputs a non-number,
752 we don't want to quit, we just want to ignore it. Change that `return` to
759 use std::cmp::Ordering;
762 println!("Guess the number!");
764 let secret_number = (rand::random::<u32>() % 100) + 1;
766 println!("The secret number is: {}", secret_number);
770 println!("Please input your guess.");
772 let input = old_io::stdin().read_line()
774 .expect("Failed to read line");
775 let input_num: Result<u32, _> = input.trim().parse();
777 let num = match input_num {
780 println!("Please input a number!");
786 println!("You guessed: {}", num);
788 match cmp(num, secret_number) {
789 Ordering::Less => println!("Too small!"),
790 Ordering::Greater => println!("Too big!"),
792 println!("You win!");
799 fn cmp(a: u32, b: u32) -> Ordering {
800 if a < b { Ordering::Less }
801 else if a > b { Ordering::Greater }
802 else { Ordering::Equal }
806 Now we should be good! Let's try:
810 Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
811 Running `target/guessing_game`
813 The secret number is: 61
814 Please input your guess.
818 Please input your guess.
822 Please input your guess.
824 Please input a number!
825 Please input your guess.
831 Awesome! With one tiny last tweak, we have finished the guessing game. Can you
832 think of what it is? That's right, we don't want to print out the secret number.
833 It was good for testing, but it kind of ruins the game. Here's our final source:
838 use std::cmp::Ordering;
841 println!("Guess the number!");
843 let secret_number = (rand::random::<u32>() % 100) + 1;
847 println!("Please input your guess.");
849 let input = old_io::stdin().read_line()
851 .expect("Failed to read line");
852 let input_num: Result<u32, _> = input.trim().parse();
854 let num = match input_num {
857 println!("Please input a number!");
863 println!("You guessed: {}", num);
865 match cmp(num, secret_number) {
866 Ordering::Less => println!("Too small!"),
867 Ordering::Greater => println!("Too big!"),
869 println!("You win!");
876 fn cmp(a: u32, b: u32) -> Ordering {
877 if a < b { Ordering::Less }
878 else if a > b { Ordering::Greater }
879 else { Ordering::Equal }
885 At this point, you have successfully built the Guessing Game! Congratulations!
887 You've now learned the basic syntax of Rust. All of this is relatively close to
888 various other programming languages you have used in the past. These
889 fundamental syntactical and semantic elements will form the foundation for the
890 rest of your Rust education.
892 Now that you're an expert at the basics, it's time to learn about some of
893 Rust's more unique features.