mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-25 05:14:27 +00:00
Auto merge of #23522 - steveklabnik:gh22518, r=Manishearth
When investigating #22518, this chapter is really the only part that has `rand`, and the rest still works without it. We should have some examples like this, but for now, it's more important to be right than perfect.
This commit is contained in:
commit
cbc660bd1c
@ -14,7 +14,6 @@
|
||||
* [Strings](strings.md)
|
||||
* [Arrays, Vectors, and Slices](arrays-vectors-and-slices.md)
|
||||
* [Standard Input](standard-input.md)
|
||||
* [Guessing Game](guessing-game.md)
|
||||
* [II: Intermediate Rust](intermediate.md)
|
||||
* [Crates and Modules](crates-and-modules.md)
|
||||
* [Testing](testing.md)
|
||||
|
@ -1,893 +0,0 @@
|
||||
% Guessing Game
|
||||
|
||||
Okay! We've got the basics of Rust down. Let's write a bigger program.
|
||||
|
||||
For our first project, we'll implement a classic beginner programming problem:
|
||||
the guessing game. Here's how it works: Our program will generate a random
|
||||
integer between one and a hundred. It will then prompt us to enter a guess.
|
||||
Upon entering our guess, it will tell us if we're too low or too high. Once we
|
||||
guess correctly, it will congratulate us. Sound good?
|
||||
|
||||
## Set up
|
||||
|
||||
Let's set up a new project. Go to your projects directory. Remember how we
|
||||
had to create our directory structure and a `Cargo.toml` for `hello_world`? Cargo
|
||||
has a command that does that for us. Let's give it a shot:
|
||||
|
||||
```{bash}
|
||||
$ cd ~/projects
|
||||
$ cargo new guessing_game --bin
|
||||
$ cd guessing_game
|
||||
```
|
||||
|
||||
We pass the name of our project to `cargo new`, and then the `--bin` flag,
|
||||
since we're making a binary, rather than a library.
|
||||
|
||||
Check out the generated `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
|
||||
name = "guessing_game"
|
||||
version = "0.0.1"
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
```
|
||||
|
||||
Cargo gets this information from your environment. If it's not correct, go ahead
|
||||
and fix that.
|
||||
|
||||
Finally, Cargo generated a "Hello, world!" for us. Check out `src/main.rs`:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
println!("Hello, world!")
|
||||
}
|
||||
```
|
||||
|
||||
Let's try compiling what Cargo gave us:
|
||||
|
||||
```{bash}
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
```
|
||||
|
||||
Excellent! Open up your `src/main.rs` again. We'll be writing all of
|
||||
our code in this file. We'll talk about multiple-file projects later on in the
|
||||
guide.
|
||||
|
||||
Before we move on, let me show you one more Cargo command: `run`. `cargo run`
|
||||
is kind of like `cargo build`, but it also then runs the produced executable.
|
||||
Try it out:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Great! The `run` command comes in handy when you need to rapidly iterate on a project.
|
||||
Our game is just such a project, we need to quickly test each iteration before moving on to the next one.
|
||||
|
||||
## Processing a Guess
|
||||
|
||||
Let's get to it! The first thing we need to do for our guessing game is
|
||||
allow our player to input a guess. Put this in your `src/main.rs`:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
You've seen this code before, when we talked about standard input. We
|
||||
import the `std::old_io` module with `use`, and then our `main` function contains
|
||||
our program's logic. We print a little message announcing the game, ask the
|
||||
user to input a guess, get their input, and then print it out.
|
||||
|
||||
Because we talked about this in the section on standard I/O, I won't go into
|
||||
more details here. If you need a refresher, go re-read that section.
|
||||
|
||||
## Generating a secret number
|
||||
|
||||
Next, we need to generate a secret number. To do that, we need to use Rust's
|
||||
random number generation, which we haven't talked about yet. Rust includes a
|
||||
bunch of interesting functions in its standard library. If you need a bit of
|
||||
code, it's possible that it's already been written for you! In this case,
|
||||
we do know that Rust has random number generation, but we don't know how to
|
||||
use it.
|
||||
|
||||
Enter the docs. Rust has a page specifically to document the standard library.
|
||||
You can find that page [here](../std/index.html). There's a lot of information on
|
||||
that page, but the best part is the search bar. Right up at the top, there's
|
||||
a box that you can enter in a search term. The search is pretty primitive
|
||||
right now, but is getting better all the time. If you type "random" in that
|
||||
box, the page will update to [this one](../std/index.html?search=random). The very
|
||||
first result is a link to [`std::rand::random`](../std/rand/fn.random.html). If we
|
||||
click on that result, we'll be taken to its documentation page.
|
||||
|
||||
This page shows us a few things: the type signature of the function, some
|
||||
explanatory text, and then an example. Let's try to modify our code to add in the
|
||||
`random` function and see what happens:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random() % 100) + 1; // secret_number: i32
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
The first thing we changed was to `use std::rand`, as the docs
|
||||
explained. We then added in a `let` expression to create a variable binding
|
||||
named `secret_number`, and we printed out its result.
|
||||
|
||||
Also, you may wonder why we are using `%` on the result of `rand::random()`.
|
||||
This operator is called *modulo*, and it returns the remainder of a division.
|
||||
By taking the modulo of the result of `rand::random()`, we're limiting the
|
||||
values to be between 0 and 99. Then, we add one to the result, making it from 1
|
||||
to 100. Using modulo can give you a very, very small bias in the result, but
|
||||
for this example, it is not important.
|
||||
|
||||
Let's try to compile this using `cargo build`:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:7:26: 7:34 error: the type of this value must be known in this context
|
||||
src/main.rs:7 let secret_number = (rand::random() % 100) + 1;
|
||||
^~~~~~~~
|
||||
error: aborting due to previous error
|
||||
```
|
||||
|
||||
It didn't work! Rust says "the type of this value must be known in this
|
||||
context." What's up with that? Well, as it turns out, `rand::random()` can
|
||||
generate many kinds of random values, not just integers. And in this case, Rust
|
||||
isn't sure what kind of value `random()` should generate. So we have to help
|
||||
it. With number literals, we can just add an `i32` onto the end to tell Rust they're
|
||||
integers, but that does not work with functions. There's a different syntax,
|
||||
and it looks like this:
|
||||
|
||||
```{rust,ignore}
|
||||
rand::random::<i32>();
|
||||
```
|
||||
|
||||
This says "please give me a random `i32` value." We can change our code to use
|
||||
this hint:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<i32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
Try running our new program a few times:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 7
|
||||
Please input your guess.
|
||||
4
|
||||
You guessed: 4
|
||||
$ ./target/guessing_game
|
||||
Guess the number!
|
||||
The secret number is: 83
|
||||
Please input your guess.
|
||||
5
|
||||
You guessed: 5
|
||||
$ ./target/guessing_game
|
||||
Guess the number!
|
||||
The secret number is: -29
|
||||
Please input your guess.
|
||||
42
|
||||
You guessed: 42
|
||||
```
|
||||
|
||||
Wait. Negative 29? We wanted a number between one and a hundred! We have two
|
||||
options here: we can either ask `random()` to generate an unsigned integer, which
|
||||
can only be positive, or we can use the `abs()` function. Let's go with the
|
||||
unsigned integer approach. If we want a random positive number, we should ask for
|
||||
a random positive number. Our code looks like this now:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
And trying it out:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 57
|
||||
Please input your guess.
|
||||
3
|
||||
You guessed: 3
|
||||
```
|
||||
|
||||
Great! Next up: let's compare our guess to the secret guess.
|
||||
|
||||
## Comparing guesses
|
||||
|
||||
If you remember, earlier in the guide, we made a `cmp` function that compared
|
||||
two numbers. Let's add that in, along with a `match` statement to compare our
|
||||
guess to the secret number:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
|
||||
match cmp(input, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
If we try to compile, we'll get some errors:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
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)
|
||||
src/main.rs:20 match cmp(input, secret_number) {
|
||||
^~~~~
|
||||
src/main.rs:20:22: 20:35 error: mismatched types: expected `i32` but found `u32` (expected i32 but found u32)
|
||||
src/main.rs:20 match cmp(input, secret_number) {
|
||||
^~~~~~~~~~~~~
|
||||
error: aborting due to 2 previous errors
|
||||
```
|
||||
|
||||
This often happens when writing Rust programs, and is one of Rust's greatest
|
||||
strengths. You try out some code, see if it compiles, and Rust tells you that
|
||||
you've done something wrong. In this case, our `cmp` function works on integers,
|
||||
but we've given it unsigned integers. In this case, the fix is easy, because
|
||||
we wrote the `cmp` function! Let's change it to take `u32`s:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
|
||||
match cmp(input, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
And try compiling again:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
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)
|
||||
src/main.rs:20 match cmp(input, secret_number) {
|
||||
^~~~~
|
||||
error: aborting due to previous error
|
||||
```
|
||||
|
||||
This error is similar to the last one: we expected to get a `u32`, but we got
|
||||
a `String` instead! That's because our `input` variable is coming from the
|
||||
standard input, and you can guess anything. Try it:
|
||||
|
||||
```bash
|
||||
$ ./target/guessing_game
|
||||
Guess the number!
|
||||
The secret number is: 73
|
||||
Please input your guess.
|
||||
hello
|
||||
You guessed: hello
|
||||
```
|
||||
|
||||
Oops! Also, you'll note that we just ran our program even though it didn't compile.
|
||||
This works because the older version we did successfully compile was still lying
|
||||
around. Gotta be careful!
|
||||
|
||||
Anyway, we have a `String`, but we need a `u32`. What to do? Well, there's
|
||||
a function for that:
|
||||
|
||||
```{rust,ignore}
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.parse();
|
||||
```
|
||||
|
||||
The `parse` function takes in a `&str` value and converts it into something.
|
||||
We tell it what kind of something with a type hint. Remember our type hint with
|
||||
`random()`? It looked like this:
|
||||
|
||||
```{rust,ignore}
|
||||
rand::random::<u32>();
|
||||
```
|
||||
|
||||
There's an alternate way of providing a hint too, and that's declaring the type
|
||||
in a `let`:
|
||||
|
||||
```{rust,ignore}
|
||||
let x: u32 = rand::random();
|
||||
```
|
||||
|
||||
In this case, we say `x` is a `u32` explicitly, so Rust is able to properly
|
||||
tell `random()` what to generate. In a similar fashion, both of these work:
|
||||
|
||||
```{rust,ignore}
|
||||
let input_num_option = "5".parse::<u32>().ok(); // input_num: Option<u32>
|
||||
let input_num_result: Result<u32, _> = "5".parse(); // input_num: Result<u32, <u32 as FromStr>::Err>
|
||||
```
|
||||
|
||||
Above, we're converting the `Result` returned by `parse` to an `Option` by using
|
||||
the `ok` method as well. Anyway, with us now converting our input to a number,
|
||||
our code looks like this:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.parse();
|
||||
|
||||
println!("You guessed: {:?}", input_num);
|
||||
|
||||
match cmp(input_num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
Let's try it out!
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:21:15: 21:24 error: mismatched types: expected `u32`, found `core::result::Result<u32, core::num::ParseIntError>` (expected u32, found enum `core::result::Result`) [E0308]
|
||||
src/main.rs:21 match cmp(input_num, secret_number) {
|
||||
^~~~~~~~~
|
||||
error: aborting due to previous error
|
||||
```
|
||||
|
||||
Oh yeah! Our `input_num` has the type `Result<u32, <some error>>`, rather than `u32`. We
|
||||
need to unwrap the Result. If you remember from before, `match` is a great way
|
||||
to do that. Try this code:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.parse();
|
||||
|
||||
let num = match input_num {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
We use a `match` to either give us the `u32` inside of the `Result`, or else
|
||||
print an error message and return. Let's give this a shot:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 17
|
||||
Please input your guess.
|
||||
5
|
||||
Please input a number!
|
||||
```
|
||||
|
||||
Uh, what? But we did!
|
||||
|
||||
... actually, we didn't. See, when you get a line of input from `stdin()`,
|
||||
you get all the input. Including the `\n` character from you pressing Enter.
|
||||
Therefore, `parse()` sees the string `"5\n"` and says "nope, that's not a
|
||||
number; there's non-number stuff in there!" Luckily for us, `&str`s have an easy
|
||||
method we can use defined on them: `trim()`. One small modification, and our
|
||||
code looks like this:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
Let's try it!
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 58
|
||||
Please input your guess.
|
||||
76
|
||||
You guessed: 76
|
||||
Too big!
|
||||
```
|
||||
|
||||
Nice! You can see I even added spaces before my guess, and it still figured
|
||||
out that I guessed 76. Run the program a few times, and verify that guessing
|
||||
the number works, as well as guessing a number too small.
|
||||
|
||||
The Rust compiler helped us out quite a bit there! This technique is called
|
||||
"leaning on the compiler", and it's often useful when working on some code.
|
||||
Let the error messages help guide you towards the correct types.
|
||||
|
||||
Now we've got most of the game working, but we can only make one guess. Let's
|
||||
change that by adding loops!
|
||||
|
||||
## Looping
|
||||
|
||||
As we already discussed, the `loop` keyword gives us an infinite loop.
|
||||
Let's add that in:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
And try it out. But wait, didn't we just add an infinite loop? Yup. Remember
|
||||
that `return`? If we give a non-number answer, we'll `return` and quit. Observe:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 59
|
||||
Please input your guess.
|
||||
45
|
||||
You guessed: 45
|
||||
Too small!
|
||||
Please input your guess.
|
||||
60
|
||||
You guessed: 60
|
||||
Too big!
|
||||
Please input your guess.
|
||||
59
|
||||
You guessed: 59
|
||||
You win!
|
||||
Please input your guess.
|
||||
quit
|
||||
Please input a number!
|
||||
```
|
||||
|
||||
Ha! `quit` actually quits. As does any other non-number input. Well, this is
|
||||
suboptimal to say the least. First, let's actually quit when you win the game:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
By adding the `return` line after the `You win!`, we'll exit the program when
|
||||
we win. We have just one more tweak to make: when someone inputs a non-number,
|
||||
we don't want to quit, we just want to ignore it. Change that `return` to
|
||||
`continue`:
|
||||
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("Please input a number!");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
Now we should be good! Let's try:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 61
|
||||
Please input your guess.
|
||||
10
|
||||
You guessed: 10
|
||||
Too small!
|
||||
Please input your guess.
|
||||
99
|
||||
You guessed: 99
|
||||
Too big!
|
||||
Please input your guess.
|
||||
foo
|
||||
Please input a number!
|
||||
Please input your guess.
|
||||
61
|
||||
You guessed: 61
|
||||
You win!
|
||||
```
|
||||
|
||||
Awesome! With one tiny last tweak, we have finished the guessing game. Can you
|
||||
think of what it is? That's right, we don't want to print out the secret number.
|
||||
It was good for testing, but it kind of ruins the game. Here's our final source:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::old_io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<u32>() % 100) + 1;
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = old_io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Result<u32, _> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("Please input a number!");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: u32, b: u32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
## Complete!
|
||||
|
||||
At this point, you have successfully built the Guessing Game! Congratulations!
|
||||
|
||||
You've now learned the basic syntax of Rust. All of this is relatively close to
|
||||
various other programming languages you have used in the past. These
|
||||
fundamental syntactical and semantic elements will form the foundation for the
|
||||
rest of your Rust education.
|
||||
|
||||
Now that you're an expert at the basics, it's time to learn about some of
|
||||
Rust's more unique features.
|
Loading…
Reference in New Issue
Block a user