]> git.lizzy.rs Git - rust.git/commitdiff
TRPL: guessing game
authorSteve Klabnik <steve@steveklabnik.com>
Sun, 3 May 2015 17:09:49 +0000 (13:09 -0400)
committerSteve Klabnik <steve@steveklabnik.com>
Tue, 5 May 2015 20:54:34 +0000 (16:54 -0400)
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
src/doc/trpl/guessing-game.md [new file with mode: 0644]
src/doc/trpl/learn-rust.md
src/doc/trpl/method-syntax.md

index 695dc42cb6418a7d9694fe07f48235009bcf8c5d..fb04b323cb05cb1f517b47e35b59f91b35aec49f 100644 (file)
@@ -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 (file)
index 0000000..431b7dc
--- /dev/null
@@ -0,0 +1,999 @@
+% 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. Sounds 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.
+
+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 bit of text.
+
+[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].
+
+[iostdin]: ../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 allows you to 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 lines. 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, three 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][ok] returns a value which has another method defined on it:
+`expect()`. The [`expect()` method][expect] 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.
+
+[ok]: ../std/result/enum.Result.html#method.ok
+[expect]: ../std/option/enum.Option.html#method.expect
+[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 yet 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="0.3.0"
+```
+
+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
+[Semantic Versioning][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][doccargo] and [its
+ecosystem][doccratesio], 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.
+
+[doccargo]: http://doc.crates.io
+[doccratesio]: http://doc.crates.io/crates-io.html
+
+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, five 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], short for ‘enumeration’, 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 `u32`. 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][parse] 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.
+
+[parse]: ../std/primitive.str.html#method.parse
+[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,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);
+
+    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 '<main>' 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,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);
+
+    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!"),
+                break;
+            }
+        }
+    }
+}
+```
+
+By adding the `break` line after the `You win!`, we’ll exit the loop when we
+win. Exiting the loop also means exiting the program, since it’s the last
+thing in `main()`. 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,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);
+
+    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!");
+                break;
+            }
+        }
+    }
+}
+```
+
+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,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);
+
+    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!");
+                break;
+            }
+        }
+    }
+}
+```
+
+# 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.
index 3d8ef8090bfb79808745453b8c3b0ca9953fc24b..1a02bc95e9d9c84247ff01c674415697457a0b04 100644 (file)
@@ -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
index 1445d39fe873876f48632571c306282c8f1eb155..ed4e9dd359b6d048159437bab7f4dfa466ca0111 100644 (file)
@@ -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