From 05a875795af04859d9adf645bf5ad4a157958c3a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sun, 3 May 2015 13:09:49 -0400 Subject: [PATCH] TRPL: guessing game This also made me realize that I wasn't using the correct term, 'associated functions', rather than 'static methods'. So I corrected that in the method syntax chapter. --- src/doc/trpl/SUMMARY.md | 1 + src/doc/trpl/guessing-game.md | 991 ++++++++++++++++++++++++++++++++++ src/doc/trpl/learn-rust.md | 9 +- src/doc/trpl/method-syntax.md | 7 +- 4 files changed, 1003 insertions(+), 5 deletions(-) create mode 100644 src/doc/trpl/guessing-game.md diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index 695dc42cb6418..fb04b323cb05c 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -5,6 +5,7 @@ * [Hello, world!](hello-world.md) * [Hello, Cargo!](hello-cargo.md) * [Learn Rust](learn-rust.md) + * [Guessing Game](guessing-game.md) * [Effective Rust](effective-rust.md) * [The Stack and the Heap](the-stack-and-the-heap.md) * [Testing](testing.md) diff --git a/src/doc/trpl/guessing-game.md b/src/doc/trpl/guessing-game.md new file mode 100644 index 0000000000000..34eb077dde84d --- /dev/null +++ b/src/doc/trpl/guessing-game.md @@ -0,0 +1,991 @@ +% Guessing Game + +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 "] +``` + +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. + +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/debug/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::io; + +fn main() { + println!("Guess the number!"); + + println!("Please input your guess."); + + let mut guess = String::new(); + + let input = io::stdin().read_line(&mut guess) + .ok() + .expect("Failed to read line"); + + println!("You guessed: {}", input); +} +``` + +There’s a lot here! Let’s go over it, bit by bit. + +```rust,ignore +use std::io; +``` + +We’ll need to take user input, and then print the result as output. As such, we +need the `io` library from the standard library. Rust only imports a few things +into every program, [the ‘prelude’][prelude]. If it’s not in the prelude, +you’ll have to `use` it directly. + +[prelude]: ../std/prelude/index.html + +```rust,ignore +fn main() { +``` + +As you’ve seen before, the `main()` function is the entry point into your +program. The `fn` syntax declares a new function, the `()`s indicate that +there are no arguments, and `{` starts the body of the function. Because +we didn’t include a return type, it’s assumed to be `()`, an empty +[tuple][tuples]. + +[tuples]: primitive-types.html#tuples + +```rust,ignore + println!("Guess the number!"); + + println!("Please input your guess."); +``` + +We previously learned that `println!()` is a [macro][macros] that +prints a [string][strings] to the screen. + +[macros]: macros.html +[strings]: strings.html + +```rust,ignore + let mut guess = String::new(); +``` + +Now we’re getting interesting! There’s a lot going on in this little line. The first thing to notice is that this is a [let statement][let], which is used to create ‘variable bindings’. They take this form: + +```rust,ignore +let foo = bar; +``` + +[let]: variable-bindings.html + +This will create a new binding named `foo`, and bind it to the value `bar`. In +many languages, this is called a ‘variable’, but Rust’s variable bindings have +a few tricks up their sleeves. + +For example, they’re [immutable][immutable] by default. That’s why our example +uses `mut`: it makes a binding mutable, rather than immutable. `let` doesn’t +take a name on the left hand side, it actually accepts a +‘[pattern][patterns]’. We’ll use patterns more later. It’s easy enough +to use for now: + +``` +let foo = 5; // immutable. +let mut bar = 5; // mutable +``` + +[immutable]: mutability.html +[patterns]: patterns.html + +Oh, and `//` will start a comment, until the end of the line. Rust ignores +everything in [comments][comments]. + +[comments]: comments.html + +So now we know that `let mut guess` will introduce a mutable binding named +`guess`, but we have to look at the other side of the `=` for what it’s +bound to: `String::new()`. + +`String` is a string type, provided by the standard library. A +[`String`][string] is a growable, UTF-8 encoded string. + +[string]: ../std/string/struct.String.html + +The `::new()` syntax is uses `::` because this is an ‘associated function’ of +a particular type. That is to say, it’s associated with `String` itself, +rather than a particular instance of a `String`. Some languages call this a +‘static method’. + +This function is named `new()`, because it creates a new, empty `String`. +You’ll find a `new()` function on many types, as it’s a common name for making +a new value of some kind. + +Let’s move forward: + +```rust,ignore + io::stdin().read_line(&mut guess) + .ok() + .expect("Failed to read line"); +``` + +That’s a lot more! Let’s go bit-by-bit. The first line has two parts. Here’s +the first: + +```rust,ignore +io::stdin() +``` + +Remember how we `use`d `std::io` on the first line of the program? We’re now +calling an associated function on it. If we didn’t `use std::io`, we could +have written this line as `std::io::stdin()`. + +This particular function returns a handle to the standard input for your +terminal. More specifically, a [std::io::Stdin][iostdin]. + +[stdin]: http://doc.rust-lang.org/nightly/std/io/struct.Stdin.html + +The next part will use this handle to get input from the user: + +```rust,ignore +.read_line(&mut guess) +``` + +Here, we call the [`read_line()`][read_line] method on our handle. +[Method][method]s are like associated functions, but are only available on a +particular instance of a type, rather than the type itself. We’re also passing +one argument to `read_line()`: `&mut guess`. + +[read_line]: ../std/io/struct.Stdin.html#method.read_line +[method]: methods.html + +Remember how we bound `guess` above? We said it was mutable. However, +`read_line` doesn’t take a `String` as an argument: it takes a `&mut String`. +Rust has a feature called ‘[references][references]’, which let you have +multiple references to one piece of data, which can reduce copying. References +are a complex feature, as one of Rust’s major selling points is how safe +and easy it is to use references. We don’t need to know a lot of those +details to finish our program right now, though. For now, all we need +to know is that like `let` bindings, references are immutable by default. +Hence, we need to write `&mut guess`, rather than `&guess`. + +Why does `read_line()` take a mutable reference to a string? Its job is +to take what the user types into standard input, and place that into a +string. So it takes that string as an argument, and in order to add +the input, it needs to be mutable. + +[references]: references-and-borrowing.html + +But we’re not quite done with this line of code, though. While it’s +a single line of text, it’s only the first part of the single logical line of +code: + +```rust,ignore + .ok() + .expect("Failed to read line"); +``` + +When you call a method with the `.foo()` syntax, you may introduce a newline +and other whitespace. This helps you split up long tlines. We _could_ have +done: + +```rust,ignore + io::stdin().read_line(&mut guess).ok().expect("failed to read line"); +``` + +But that gets hard to read. So we’ve split it up, theree lines for three +method calls. We already talked about `read_line()`, but what about `ok()` +and `expect()`? Well, we already mentioned that `read_line()` puts what +the user types into the `&mut String` we pass it. But it also returns +a value: in this case, an [`io::Result`][ioresult]. Rust has a number of +types named `Result` in its standard library: a generic [`Result`][result], +and then specific versions for sub-libraries, like `io::Result`. + +[ioresult]: ../std/io/type.Result.html +[result]: ../std/result/enum.Result.html + +The purpose of these `Result` types is to encode error handling information. +Values of the `Result` type, like any type, have methods defined on them. In +this case, `io::Result` has an `ok()` method, which says ‘we want to assume +this value is a successful one. If not, just throw away the error +information’. Why throw it away? Well, for a basic program, we just want to +print a generic error, as basically any issue means we can’t continue. The +`ok()` method returns a value which has another method defined on it: +`expect()`. The `expect()` method takes a value it’s called on, and if it +isn’t a successful one, [`panic!`][panic]s with a message you passed you +passed it. A `panic!` like this will cause our program to crash, displaying +the message. + +[panic]: error-handling.html + +If we leave off calling these two methods, our program will compile, but +we’ll get a warning: + +```bash +$ cargo build + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) +src/main.rs:10:5: 10:39 warning: unused result which must be used, +#[warn(unused_must_use)] on by default +src/main.rs:10 io::stdin().read_line(&mut guess); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``` + +Rust warns us that we haven’t used the `Result` value. This warning comes from +a special annotation that `io::Result` has. Rust is trying to tell you that +you haven’t handled a possible error. The right way to suppress the error is +to actually write error handling. Luckily, if we just want to crash if there’s +a problem, we can use these two little methods. If we can recover from the +error somehow, we’d do something else, but we’ll save that for a future +project. + +There’s just one line of this first example left: + +```rust,ignore + println!("You guessed: {}", input); +} +``` + +This prints out the string we saved our input in. The `{}`s are a placeholder, +and so we pass it `input` as an argument. If we had multiple `{}`s, we would +pass multiple arguments: + +```rust +let x = 5; +let y = 10; + +println!("x and y: {} and {}", x, y); +``` + +Easy. + +Anyway, that’s the tour. We can run what we have with `cargo run`: + +```bash +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/debug/guessing_game` +Guess the number! +Please input your guess. +6 +You guessed: 6 +``` + +All right! Our first part is done: we can get input from the keyboard, +and then print it back out. + +# Generating a secret number + +Next, we need to generate a secret number. Rust does not include random +number functionality in its standard library. The Rust team does, however, +provide a [`rand` crate][randcrate]. A ‘crate’ is a package of Rust code. +We’ve been building a ‘binary crate’, which is an executable. `rand` is +a ‘library crate’, which contains code that’s intended to be used with +other programs. + +[randcrate]: https://crates.io/crates/rand + +Using external crates is where Cargo really shines. Before we can write +the code using `rand`, we need to modify our `Cargo.toml`. Open it up, and +add these few lines at the bottom: + +```toml +[dependencies] + +rand="*" +``` + +The `[dependencies]` section of `Cargo.toml` is like the `[package]` section: +everything that follows it is part of it, until the next section starts. +Cargo uses the dependencies section to know what dependencies on external +crates you have, and what versions you require. In this case, we’ve used `*`, +which means that we’ll use the latest version of `rand`. Cargo understands +[SemVer][semver], which is a standard for writing version numbers. If we +wanted a specific version or range of versions, we could be more specific +here. [Cargo’s documentation][cargodoc] contains more details. + +[semver]: http://semver.org +[cargodoc]: http://doc.crates.io/crates-io.html + +Now, without changing any of our code, let’s build our project: + +```bash +$ cargo build + Updating registry `https://github.com/rust-lang/crates.io-index` + Downloading rand v0.3.8 + Downloading libc v0.1.6 + Compiling libc v0.1.6 + Compiling rand v0.3.8 + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) +``` + +(You may see different versions, of course.) + +Lots of new output! Now that we have an external dependency, Cargo fetches the +latest versions of everything from the registry, which is a copy of data from +[Crates.io][cratesio]. Crates.io is where people in the Rust ecosystem +post their open source Rust projects for others to use. + +[cratesio]: https://crates.io + +After updating the registry, Cargo checks our `[dependencies]` and downloads +any we don’t have yet. In this case, while we only said we wanted to depend on +`rand`, we’ve also grabbed a copy of `libc`. This is because `rand` depends on +`libc` to work. After downloading them, it compiles them, and then compiles +our project. + +If we run `cargo build` again, we’ll get different output: + +```bash +$ cargo build +``` + +That’s right, no output! Cargo knows that our project has been built, and that +all of its dependencies are built, and so there’s no reason to do all that +stuff. With nothing to do, it simply exits. If we open up `src/main.rs` again, +make a trivial change, and then save it again, we’ll just see one line: + +```bash +$ cargo build + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) +``` + +So, we told Cargo we wanted any version of `rand`, and so it fetched the +latest version at the time this was written, `v0.3.8`. But what happens +when next week, version `v0.4.0` comes out, which changes something with +`rand`, and it includes a breaking change? After all, a `v0.y.z` version +in SemVer can change every release. + +The answer to this problem is the `Cargo.lock` file you’ll now find in your +project directory. When you build your project for the first time, Cargo +figures out all of the versions that fit your criteria, and then writes them +to the `Cargo.lock` file. When you build your project in the future, Cargo +will see that the `Cargo.lock` file exists, and then use that specific version +rather than do all the work of figuring out versions again. This lets you +have a repeatable build automatically. + +What about when we _do_ want to use `v0.4.0`? Cargo has another command, +`update`, which says ‘ignore the lock, figure out all the latest versions that +fit what we’ve specified. If that works, write those versions out to the lock +file’. + +There’s a lot more to say about Cargo and its ecosystem, but for now, that’s +all we need to know. Cargo makes it really easy to re-use libraries, and so +Rustaceans tend to write smaller projects which are assembled out of a number +of sub-packages. + +Let’s get on to actually _using_ `rand`. Here’s our next step: + +```rust,ignore +extern crate rand; + +use std::io; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + println!("You guessed: {}", guess); +} +``` + +The first thing we’ve done is change the first line. It now says +`extern crate rand`. Because we declared `rand` in our `[dependencies]`, we +can use `extern crate` to let Rust know we’ll be making use of it. This also +does the equivalent of a `use rand;` as well, so we can make use of anything +in the `rand` crate by prefixing it with `rand::`. + +Next, we added another `use` line: `use rand::Rng`. We’re going to use a +method in a moment, and it requires that `Rng` be in scope to work. The basic +idea is this: methods are defined on something called ‘traits’, and for the +method to work, it needs the trait to be in scope. For more about the +details, read the [traits][traits] section. + +[traits]: traits.html + +There are two other lines we added, in the middle: + +```rust,ignore + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); +``` + +We use the `rand::thread_rng()` function to get a copy of the random number +generator, which is local to the particular [thread][concurrency] of execution +we’re in. Because we `use rand::Rng`’d above, it has a `gen_range()` method +available. This method takes two arguments, and generates a number between +them. It’s inclusive on the lower bound, but exclusive on the upper bound, +so we need `1` and `101` to get a number between one and a hundred. + +[concurrency]: concurrency.html + +The second line just prints out the secret number. This is useful while +we’re developing our program, so we can easily test it out. But we’ll be +deleting it for the final version. It’s not much of a game if it prints out +the answer when you start it up! + +Try running our new program a few times: + +```bash +$ cargo run + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) + Running `target/debug/guessing_game` +Guess the number! +The secret number is: 7 +Please input your guess. +4 +You guessed: 4 +$ cargo run + Running `target/debug/guessing_game` +Guess the number! +The secret number is: 83 +Please input your guess. +5 +You guessed: 5 +``` + +Great! Next up: let’s compare our guess to the secret guess. + +# Comparing guesses + +Now that we’ve got user input, let’s compare our guess to the random guess. +Here’s our next step, though it doesn’t quite work yet: + +```rust,ignore +extern crate rand; + +use std::io; +use std::cmp::Ordering; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => println!("You win!"), + } +} +``` + +A few new bits here. The first is another `use`. We bring a type called +`std::cmp::Ordering` into scope. Then, four new lines at the bottom that use +it: + +```rust,ignore +match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => println!("You win!"), +} +``` + +The `cmp()` method can be called on anything that can be compared, and it +takes a reference to the thing you want to compare it to. It returns the +`Ordering` type we `use`d earlier. We use a [`match`][match] statement to +determine exactly what kind of `Ordering` it is. `Ordering` is an +[`enum`][enum], which looks like this: + +```rust +enum Foo { + Bar, + Baz, +} +``` + +[match]: match.html +[enum]: enums.html + +With this definition, anything of type `Foo` can be either a +`Foo::Bar` or a `Foo::Baz`. We use the `::` to indicate the +namespace for a particular `enum` variant. + +The [`Ordering`][ordering] enum has three possible variants: `Less`, `Equal`, +and `Greater`. The `match` statement takes a value of a type, and lets you +create an ‘arm’ for each possible value. Since we have three types of +`Ordering`, we have three arms: + +```rust,ignore +match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => println!("You win!"), +} +``` + +[ordering]: ../std/cmp/enum.Ordering.html + +If it’s `Less`, we print `Too small!`, if it’s `Greater`, `Too big!`, and if +`Equal`, `You win!`. `match` is really useful, and is used often in Rust. + +I did mention that this won’t quite work yet, though. Let’s try it: + +```bash +$ cargo build + Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game) +src/main.rs:28:21: 28:35 error: mismatched types: + expected `&collections::string::String`, + found `&_` +(expected struct `collections::string::String`, + found integral variable) [E0308] +src/main.rs:28 match guess.cmp(&secret_number) { + ^~~~~~~~~~~~~~ +error: aborting due to previous error +Could not compile `guessing_game`. +``` + +Whew! This is a big error. The core of it is that we have ‘mismatched types’. +Rust has a strong, static type system. However, it also has type inference. +When we wrote `let guess = String::new()`, Rust was able to infer that `guess` +should be a `String`, and so it doesn’t make us write out the type. And with +our `secret_number`, there are a number of types which can have a value +between one and a hundred: `i32`, a thirty-two-bit number, or `u32`, an +unsigned thirty-two-bit number, or `i64`, a sixty-four-bit number. Or others. +So far, that hasn’t mattered, and so Rust defaults to an `i32`. However, here, +Rust doesn’t know how to compare the `guess` and the `secret_number`. They +need to be the same type. Ultimately, we want to convert the `String` we +read as input into a real number type, for comparison. We can do that +with three more lines. Here’s our new program: + +```rust,ignore +extern crate rand; + +use std::io; +use std::cmp::Ordering; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + let guess: u32 = guess.trim().parse() + .ok() + .expect("Please type a number!"); + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => println!("You win!"), + } +} +``` + +The new three lines: + +```rust,ignore + let guess: u32 = guess.trim().parse() + .ok() + .expect("Please type a number!"); +``` + +Wait a minute, I thought we already had a `guess`? We do, but Rust allows us +to ‘shadow’ the previous `guess` with a new one. This is often used in this +exact situation, where `guess` starts as a `String`, but we want to convert it +to an `i32`. Shadowing lets us re-use the `guess` name, rather than forcing us +to come up with two unique names like `guess_str` and `guess`, or something +else. + +We bind `guess` to an expression that looks like something we wrote earlier: + +```rust,ignore +guess.trim.parse() +``` + +Followed by an `ok().expect()` invocation. Here, `guess` refers to the old +`guess`, the one that was a `String` with our input in it. The `trim`()` +method on `String`s will eliminate any white space at the beginning and end of +our string. This is important, as we had to press the ‘return’ key to satisfy +`read_line()`. This means that if we type `5` and hit return, `guess` looks +like this: `5\n`. The `\n` represents ‘newline’, the enter key. `trim()` gets +rid of this, leaving our string with just the `5`. The `parse()` method on +strings parses a string into some kind of number. Since it can parse a variety +of numbers, we need to give Rust a hint as to the exact type of number we +want. Hence, `let guess: u32`. The colon (`:`) after `guess` tells Rust we’re +going to annotate its type. `u32` is an unsigned, thirty-two bit integer. Rust +has [a number of built-in number types][number], but we’ve chosen `u32`. It’s +a good default choice for a small positive numer. + +[number]: primitive-types.html#numeric-types + +Just like `read_line()`, our call to `parse()` could cause an error. What if +our string contained `A👍%`? There’d be no way to convert that to a number. As +such, we’ll do the same thing we did with `read_line()`: use the `ok()` and +`expect()` methods to crash if there’s an error. + +Let’s try our program 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: 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. + +Now we’ve got most of the game working, but we can only make one guess. Let’s +change that by adding loops! + +# Looping + +The `loop` keyword gives us an infinite loop. Let’s add that in: + +```rust,no_run +extern crate rand; + +use std::io; +use std::cmp::Ordering; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + loop { + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + let guess: u32 = guess.trim().parse() + .ok() + .expect("Please type a number!"); + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => println!("You win!"), + } + } +} +``` + +And try it out. But wait, didn’t we just add an infinite loop? Yup. Remember +our discussion about `parse()`? 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 +thread '
' panicked at 'Please type 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 +extern crate rand; + +use std::io; +use std::cmp::Ordering; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + loop { + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + let guess: u32 = guess.trim().parse() + .ok() + .expect("Please type a number!"); + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => { + println!("You win!"), + return; + } + } + } +} +``` + +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. We can do that like this: + +```rust,no_run +extern crate rand; + +use std::io; +use std::cmp::Ordering; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + loop { + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + let guess: u32 = match guess.trim().parse() { + Ok(num) => num, + Err(_) => continue, + }; + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => { + println!("You win!"), + return; + } + } + } +} +``` + +These are the lines that changed: + +```rust,ignore +let guess: u32 = match guess.trim().parse() { + Ok(num) => num, + Err(_) => continue, +}; +``` + +This is how you generally move from ‘crash on error’ to ‘actually handle the +error’, by switching from `ok().expect()` to a `match` statement. The `Result` +returned by `parse()` is an enum just like `Ordering`, but in this case, each +variant has some data associated with it: `Ok` is a success, and `Err` is a +failure. Each contains more information: the successful parsed integer, or an +error type. In this case, we `match` on `Ok(num)`, which sets the inner value +of the `Ok` to the name `num`, and then we just return it on the right-hand +side. In the `Err` case, we don’t care what kind of error it is, so we just +use `_` intead of a name. This ignores the error, and `continue` causes us +to go to the next iteration of the `loop`. + +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 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 +extern crate rand; + +use std::io; +use std::cmp::Ordering; +use rand::Rng; + +fn main() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1, 101); + + println!("The secret number is: {}", secret_number); + + loop { + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin().read_line(&mut guess) + .ok() + .expect("failed to read line"); + + let guess: u32 = match guess.trim().parse() { + Ok(num) => num, + Err(_) => continue, + }; + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => { + println!("You win!"), + return; + } + } + } +} +``` + +# Complete! + +At this point, you have successfully built the Guessing Game! Congratulations! + +This first project showed you a lot: `let`, `match`, methods, associated +functions, using external crates, and more. Our next project will show off +even more. diff --git a/src/doc/trpl/learn-rust.md b/src/doc/trpl/learn-rust.md index 3d8ef8090bfb7..1a02bc95e9d9c 100644 --- a/src/doc/trpl/learn-rust.md +++ b/src/doc/trpl/learn-rust.md @@ -1,4 +1,9 @@ % Learn Rust -This section is coming soon! It will eventually have a few tutorials with -building real Rust projects, but they are under development. +Welcome! This section has a few tutorials that teach you Rust through building +projects. You’ll get a high-level overview, but we’ll skim over the details. + +If you’d prefer a more ‘from the ground up’-style experience, check +out [Syntax and Semantics][ss]. + +[ss]: syntax-and-semantics.html diff --git a/src/doc/trpl/method-syntax.md b/src/doc/trpl/method-syntax.md index 1445d39fe8738..ed4e9dd359b6d 100644 --- a/src/doc/trpl/method-syntax.md +++ b/src/doc/trpl/method-syntax.md @@ -154,9 +154,10 @@ fn main() { } ``` -This ‘static method’ builds a new `Circle` for us. Note that static methods -are called with the `Struct::method()` syntax, rather than the `ref.method()` -syntax. +This ‘associated function’ builds a new `Circle` for us. Note that associated +functions are called with the `Struct::function()` syntax, rather than the +`ref.method()` syntax. Some other langauges call associated functions ‘static +methods’. # Builder Pattern