From 04765866436a6d89642bdd69f3d8fb1e8012a463 Mon Sep 17 00:00:00 2001 From: Jason Yeo Date: Tue, 5 May 2015 12:02:18 +0800 Subject: [PATCH 1/5] Fix indentation in while-let example --- src/doc/trpl/if-let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/trpl/if-let.md b/src/doc/trpl/if-let.md index 7173303e3b15..4872ed6a7734 100644 --- a/src/doc/trpl/if-let.md +++ b/src/doc/trpl/if-let.md @@ -65,7 +65,7 @@ loop as long as a value matches a certain pattern. It turns code like this: loop { match option { Some(x) => println!("{}", x), - _ => break, + _ => break, } } ``` From 68f5c8475a09bf000d4a0870338f7457cd4dd1cc Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Tue, 5 May 2015 21:38:06 +1000 Subject: [PATCH 2/5] Markdown edits for diagnostic errors. --- src/librustc/diagnostics.rs | 48 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 6cb39c39659e..19d2df0b486c 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -37,7 +37,7 @@ An example of an empty type is `enum Empty { }`. E0003: r##" Not-a-Number (NaN) values cannot be compared for equality and hence can never match the input to a match expression. To match against NaN values, you should -instead use the `is_nan` method in a guard, as in: x if x.is_nan() => ... +instead use the `is_nan` method in a guard, as in: `x if x.is_nan() => ...` "##, E0004: r##" @@ -71,7 +71,7 @@ failure. E0007: r##" This error indicates that the bindings in a match arm would require a value to be moved into more than one location, thus violating unique ownership. Code like -the following is invalid as it requires the entire Option to be moved +the following is invalid as it requires the entire `Option` to be moved into a variable called `op_string` while simultaneously requiring the inner String to be moved into a variable called `s`. @@ -99,10 +99,10 @@ match Some("hi".to_string()) { } ``` -The variable `s` has type String, and its use in the guard is as a variable of -type String. The guard code effectively executes in a separate scope to the body -of the arm, so the value would be moved into this anonymous scope and therefore -become unavailable in the body of the arm. Although this example seems +The variable `s` has type `String`, and its use in the guard is as a variable of +type `String`. The guard code effectively executes in a separate scope to the +body of the arm, so the value would be moved into this anonymous scope and +therefore become unavailable in the body of the arm. Although this example seems innocuous, the problem is most clear when considering functions that take their argument by value. @@ -140,7 +140,8 @@ match x { ``` You have two solutions: -1. Bind the pattern's values the same way: + +Solution #1: Bind the pattern's values the same way. ``` struct X { x: (), } @@ -153,8 +154,9 @@ match x { } ``` -2. Implement the `Copy` trait for the X structure (however, please -keep in mind that the first solution should be preferred!): +Solution #2: Implement the `Copy` trait for the `X` structure. + +However, please keep in mind that the first solution should be preferred. ``` #[derive(Clone, Copy)] @@ -258,11 +260,13 @@ functions via FFI or marked as unsafe, is potentially dangerous and disallowed by safety checks. As such, those safety checks can be temporarily relaxed by wrapping the unsafe instructions inside an `unsafe` block. For instance: +``` unsafe fn f() { return; } fn main() { unsafe { f(); } } +``` See also http://doc.rust-lang.org/book/unsafe.html "##, @@ -313,8 +317,8 @@ it around as usual. E0162: r##" An if-let pattern attempts to match the pattern, and enters the body if the -match was succesful. If the match is irrefutable (when it cannot fail to match), -use a regular `let`-binding instead. For instance: +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding instead. For instance: ``` struct Irrefutable(i32); @@ -334,8 +338,8 @@ foo(x); E0165: r##" A while-let pattern attempts to match the pattern, and enters the body if the -match was succesful. If the match is irrefutable (when it cannot fail to match), -use a regular `let`-binding inside a `loop` instead. For instance: +match was successful. If the match is irrefutable (when it cannot fail to +match), use a regular `let`-binding inside a `loop` instead. For instance: ``` struct Irrefutable(i32); @@ -374,7 +378,7 @@ match m { ``` If you don't qualify the names, the code will bind new variables named "GET" and -"POST" instead. This behavior is likely not what you want, so rustc warns when +"POST" instead. This behavior is likely not what you want, so `rustc` warns when that happens. Qualified names are good practice, and most code works well with them. But if @@ -403,16 +407,16 @@ const Y: u32 = X; "##, E0267: r##" -This error indicates the use of loop keyword (break or continue) inside a -closure but outside of any loop. Break and continue can be used as normal -inside closures as long as they are also contained within a loop. To halt the -execution of a closure you should instead use a return statement. +This error indicates the use of a loop keyword (`break` or `continue`) inside a +closure but outside of any loop. Break and continue can be used as normal inside +closures as long as they are also contained within a loop. To halt the execution +of a closure you should instead use a return statement. "##, E0268: r##" -This error indicates the use of loop keyword (break or continue) outside of a -loop. Without a loop to break out of or continue in, no sensible action can be -taken. +This error indicates the use of a loop keyword (`break` or `continue`) outside +of a loop. Without a loop to break out of or continue in, no sensible action can +be taken. "##, E0296: r##" @@ -507,7 +511,7 @@ match Some("hi".to_string()) { } ``` -The `op_string_ref` binding has type &Option<&String> in both cases. +The `op_string_ref` binding has type `&Option<&String>` in both cases. See also https://github.com/rust-lang/rust/issues/14587 "##, From ab3cb8c5ae3b9fe86faa1dfa9402145788a005f5 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 24 Apr 2015 16:43:36 -0400 Subject: [PATCH 3/5] TRPL: ownership, borrowing, and lifetimes Also, as @huonw guessed, move semantics really _does_ make more sense as a sub-chapter of ownership. --- src/doc/trpl/SUMMARY.md | 1 - src/doc/trpl/lifetimes.md | 296 ++++++++++- src/doc/trpl/move-semantics.md | 105 ---- src/doc/trpl/ownership.md | 602 +++++------------------ src/doc/trpl/references-and-borrowing.md | 335 ++++++++++++- 5 files changed, 756 insertions(+), 583 deletions(-) delete mode 100644 src/doc/trpl/move-semantics.md diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index 7ce74e86fef6..a38f6949b45b 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -26,7 +26,6 @@ * [References and Borrowing](references-and-borrowing.md) * [Lifetimes](lifetimes.md) * [Mutability](mutability.md) - * [Move semantics](move-semantics.md) * [Enums](enums.md) * [Match](match.md) * [Structs](structs.md) diff --git a/src/doc/trpl/lifetimes.md b/src/doc/trpl/lifetimes.md index cfcd8c4ee155..981286c82d79 100644 --- a/src/doc/trpl/lifetimes.md +++ b/src/doc/trpl/lifetimes.md @@ -1,3 +1,297 @@ % Lifetimes -Coming Soon! Until then, check out the [ownership](ownership.html) chapter. +This guide is one of three presenting Rust’s ownership system. This is one of +Rust’s most unique and compelling features, with which Rust developers should +become quite acquainted. Ownership is how Rust achieves its largest goal, +memory safety. There are a few distinct concepts, each with its own chapter: + +* [ownership][ownership], ownership, the key concept +* [borrowing][borrowing], and their associated feature ‘references’ +* lifetimes, which you’re reading now + +These three chapters are related, and in order. You’ll need all three to fully +understand the ownership system. + +[ownership]: ownership.html +[borrowing]: references-and-borrowing.html + +# Meta + +Before we get to the details, two important notes about the ownership system. + +Rust has a focus on safety and speed. It accomplishes these goals through many +‘zero-cost abstractions’, which means that in Rust, abstractions cost as little +as possible in order to make them work. The ownership system is a prime example +of a zero-cost abstraction. All of the analysis we’ll talk about in this guide +is _done at compile time_. You do not pay any run-time cost for any of these +features. + +However, this system does have a certain cost: learning curve. Many new users +to Rust experience something we like to call ‘fighting with the borrow +checker’, where the Rust compiler refuses to compile a program that the author +thinks is valid. This often happens because the programmer’s mental model of +how ownership should work doesn’t match the actual rules that Rust implements. +You probably will experience similar things at first. There is good news, +however: more experienced Rust developers report that once they work with the +rules of the ownership system for a period of time, they fight the borrow +checker less and less. + +With that in mind, let’s learn about lifetimes. + +# Lifetimes + +Lending out a reference to a resource that someone else owns can be +complicated, however. For example, imagine this set of operations: + +- I acquire a handle to some kind of resource. +- I lend you a reference to the resource. +- I decide I’m done with the resource, and deallocate it, while you still have + your reference. +- You decide to use the resource. + +Uh oh! Your reference is pointing to an invalid resource. This is called a +dangling pointer or ‘use after free’, when the resource is memory. + +To fix this, we have to make sure that step four never happens after step +three. The ownership system in Rust does this through a concept called +lifetimes, which describe the scope that a reference is valid for. + +When we have a function that takes a reference by argument, we can be implicit +or explicit about the lifetime of the reference: + +```rust +// implicit +fn foo(x: &i32) { +} + +// explicit +fn bar<'a>(x: &'a i32) { +} +``` + +The `'a` reads ‘the lifetime a’. Technically, every reference has some lifetime +associated with it, but the compiler lets you elide them in common cases. +Before we get to that, though, let’s break the explicit example down: + +```rust,ignore +fn bar<'a>(...) +``` + +This part declares our lifetimes. This says that `bar` has one lifetime, `'a`. +If we had two reference parameters, it would look like this: + +```rust,ignore +fn bar<'a, 'b>(...) +``` + +Then in our parameter list, we use the lifetimes we’ve named: + +```rust,ignore +...(x: &'a i32) +``` + +If we wanted an `&mut` reference, we’d do this: + +```rust,ignore +...(x: &'a mut i32) +``` + +If you compare `&mut i32` to `&'a mut i32`, they’re the same, it’s just that +the lifetime `'a` has snuck in between the `&` and the `mut i32`. We read `&mut +i32` as ‘a mutable reference to an i32’ and `&'a mut i32` as ‘a mutable +reference to an `i32` with the lifetime `'a`’. + +You’ll also need explicit lifetimes when working with [`struct`][structs]s: + +```rust +struct Foo<'a> { + x: &'a i32, +} + +fn main() { + let y = &5; // this is the same as `let _y = 5; let y = &_y;` + let f = Foo { x: y }; + + println!("{}", f.x); +} +``` + +[struct]: structs.html + +As you can see, `struct`s can also have lifetimes. In a similar way to functions, + +```rust +struct Foo<'a> { +# x: &'a i32, +# } +``` + +declares a lifetime, and + +```rust +# struct Foo<'a> { +x: &'a i32, +# } +``` + +uses it. So why do we need a lifetime here? We need to ensure that any reference +to a `Foo` cannot outlive the reference to an `i32` it contains. + +## Thinking in scopes + +A way to think about lifetimes is to visualize the scope that a reference is +valid for. For example: + +```rust +fn main() { + let y = &5; // -+ y goes into scope + // | + // stuff // | + // | +} // -+ y goes out of scope +``` + +Adding in our `Foo`: + +```rust +struct Foo<'a> { + x: &'a i32, +} + +fn main() { + let y = &5; // -+ y goes into scope + let f = Foo { x: y }; // -+ f goes into scope + // stuff // | + // | +} // -+ f and y go out of scope +``` + +Our `f` lives within the scope of `y`, so everything works. What if it didn’t? +This code won’t work: + +```rust,ignore +struct Foo<'a> { + x: &'a i32, +} + +fn main() { + let x; // -+ x goes into scope + // | + { // | + let y = &5; // ---+ y goes into scope + let f = Foo { x: y }; // ---+ f goes into scope + x = &f.x; // | | error here + } // ---+ f and y go out of scope + // | + println!("{}", x); // | +} // -+ x goes out of scope +``` + +Whew! As you can see here, the scopes of `f` and `y` are smaller than the scope +of `x`. But when we do `x = &f.x`, we make `x` a reference to something that’s +about to go out of scope. + +Named lifetimes are a way of giving these scopes a name. Giving something a +name is the first step towards being able to talk about it. + +## 'static + +The lifetime named ‘static’ is a special lifetime. It signals that something +has the lifetime of the entire program. Most Rust programmers first come across +`'static` when dealing with strings: + +```rust +let x: &'static str = "Hello, world."; +``` + +String literals have the type `&'static str` because the reference is always +alive: they are baked into the data segment of the final binary. Another +example are globals: + +```rust +static FOO: i32 = 5; +let x: &'static i32 = &FOO; +``` + +This adds an `i32` to the data segment of the binary, and `x` is a reference +to it. + +## Lifetime Elision + +Rust supports powerful local type inference in function bodies, but it’s +forbidden in item signatures to allow reasoning about the types just based in +the item signature alone. However, for ergonomic reasons a very restricted +secondary inference algorithm called “lifetime elision” applies in function +signatures. It infers only based on the signature components themselves and not +based on the body of the function, only infers lifetime parameters, and does +this with only three easily memorizable and unambiguous rules. This makes +lifetime elision a shorthand for writing an item signature, while not hiding +away the actual types involved as full local inference would if applied to it. + +When talking about lifetime elision, we use the term *input lifetime* and +*output lifetime*. An *input lifetime* is a lifetime associated with a parameter +of a function, and an *output lifetime* is a lifetime associated with the return +value of a function. For example, this function has an input lifetime: + +```rust,ignore +fn foo<'a>(bar: &'a str) +``` + +This one has an output lifetime: + +```rust,ignore +fn foo<'a>() -> &'a str +``` + +This one has a lifetime in both positions: + +```rust,ignore +fn foo<'a>(bar: &'a str) -> &'a str +``` + +Here are the three rules: + +* Each elided lifetime in a function’s arguments becomes a distinct lifetime + parameter. + +* If there is exactly one input lifetime, elided or not, that lifetime is + assigned to all elided lifetimes in the return values of that function. + +* If there are multiple input lifetimes, but one of them is `&self` or `&mut + self`, the lifetime of `self` is assigned to all elided output lifetimes. + +Otherwise, it is an error to elide an output lifetime. + +### Examples + +Here are some examples of functions with elided lifetimes. We’ve paired each +example of an elided lifetime with its expanded form. + +```rust,ignore +fn print(s: &str); // elided +fn print<'a>(s: &'a str); // expanded + +fn debug(lvl: u32, s: &str); // elided +fn debug<'a>(lvl: u32, s: &'a str); // expanded + +// In the preceding example, `lvl` doesn’t need a lifetime because it’s not a +// reference (`&`). Only things relating to references (such as a `struct` +// which contains a reference) need lifetimes. + +fn substr(s: &str, until: u32) -> &str; // elided +fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded + +fn get_str() -> &str; // ILLEGAL, no inputs + +fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs +fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is unclear + +fn get_mut(&mut self) -> &mut T; // elided +fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded + +fn args(&mut self, args: &[T]) -> &mut Command // elided +fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded + +fn new(buf: &mut [u8]) -> BufWriter; // elided +fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded +``` diff --git a/src/doc/trpl/move-semantics.md b/src/doc/trpl/move-semantics.md deleted file mode 100644 index b5bd53e1d75a..000000000000 --- a/src/doc/trpl/move-semantics.md +++ /dev/null @@ -1,105 +0,0 @@ -% Move Semantics - -An important aspect of [ownership][ownership] is ‘move semantics’. Move -semantics control how and when ownership is transferred between bindings. - -[ownership]: ownership.html - -For example, consider a type like `Vec`, which owns its contents: - -```rust -let v = vec![1, 2, 3]; -``` - -I can assign this vector to another binding: - -```rust -let v = vec![1, 2, 3]; - -let v2 = v; -``` - -But, if we try to use `v` afterwards, we get an error: - -```rust,ignore -let v = vec![1, 2, 3]; - -let v2 = v; - -println!("v[0] is: {}", v[0]); -``` - -It looks like this: - -```text -error: use of moved value: `v` -println!("v[0] is: {}", v[0]); - ^ -``` - -A similar thing happens if we define a function which takes ownership, and -try to use something after we’ve passed it as an argument: - -```rust,ignore -fn take(v: Vec) { - // what happens here isn’t important. -} - -let v = vec![1, 2, 3]; - -take(v); - -println!("v[0] is: {}", v[0]); -``` - -Same error: “use of moved value.” When we transfer ownership to something else, -we say that we’ve ‘moved’ the thing we refer to. You don’t need some sort of -special annotation here, it’s the default thing that Rust does. - -# The details - -The reason that we cannot use a binding after we’ve moved it is subtle, but -important. When we write code like this: - -```rust -let v = vec![1, 2, 3]; - -let v2 = v; -``` - -The first line creates some data for the vector on the stack, `v`. The vector’s -data, however, is stored on the heap, and so it contains a pointer to that -data. When we move `v` to `v2`, it creates a copy of that data, for `v2`. Which -would mean two pointers to the contents of the vector on the heap. That would -be a problem: it would violate Rust’s safety guarantees by introducing a data -race. Therefore, Rust forbids using `v` after we’ve done the move. - -It’s also important to note that optimizations may remove the actual copy of -the bytes, depending on circumstances. So it may not be as inefficient as it -initially seems. - -# `Copy` types - -We’ve established that when ownership is transferred to another binding, you -cannot use the original binding. However, there’s a [trait][traits] that changes this -behavior, and it’s called `Copy`. We haven’t discussed traits yet, but for now, -you can think of them as an annotation to a particular type that adds extra -behavior. For example: - -```rust -let v = 1; - -let v2 = v; - -println!("v is: {}", v); -``` - -In this case, `v` is an `i32`, which implements the `Copy` trait. This means -that, just like a move, when we assign `v` to `v2`, a copy of the data is made. -But, unlike a move, we can still use `v` afterward. This is because an `i32` -has no pointers to data somewhere else, copying it is a full copy. - -We will discuss how to make your own types `Copy` in the [traits][traits] -section. - -[traits]: traits.html diff --git a/src/doc/trpl/ownership.md b/src/doc/trpl/ownership.md index 223085cc40b8..3003156f875a 100644 --- a/src/doc/trpl/ownership.md +++ b/src/doc/trpl/ownership.md @@ -1,555 +1,207 @@ % Ownership -This guide presents Rust's ownership system. This is one of Rust's most unique -and compelling features, with which Rust developers should become quite -acquainted. Ownership is how Rust achieves its largest goal, memory safety. -The ownership system has a few distinct concepts: *ownership*, *borrowing*, -and *lifetimes*. We'll talk about each one in turn. +This guide is one of three presenting Rust’s ownership system. This is one of +Rust’s most unique and compelling features, with which Rust developers should +become quite acquainted. Ownership is how Rust achieves its largest goal, +memory safety. The there are a few distinct concepts, each with its own +chapter: + +* ownership, which you’re reading now. +* [borrowing][borrowing], and their associated feature ‘references’ +* [lifetimes][lifetimes], an advanced concept of borrowing + +These three chapters are related, and in order. You’ll need all three to fully +understand the ownership system. + +[borrowing]: references-and-borrowing.html +[lifetimes]: lifetimes.html # Meta Before we get to the details, two important notes about the ownership system. Rust has a focus on safety and speed. It accomplishes these goals through many -*zero-cost abstractions*, which means that in Rust, abstractions cost as little +‘zero-cost abstractions’, which means that in Rust, abstractions cost as little as possible in order to make them work. The ownership system is a prime example -of a zero cost abstraction. All of the analysis we'll talk about in this guide +of a zero cost abstraction. All of the analysis we’ll talk about in this guide is _done at compile time_. You do not pay any run-time cost for any of these features. However, this system does have a certain cost: learning curve. Many new users -to Rust experience something we like to call "fighting with the borrow -checker," where the Rust compiler refuses to compile a program that the author -thinks is valid. This often happens because the programmer's mental model of -how ownership should work doesn't match the actual rules that Rust implements. +to Rust experience something we like to call ‘fighting with the borrow +checker’, where the Rust compiler refuses to compile a program that the author +thinks is valid. This often happens because the programmer’s mental model of +how ownership should work doesn’t match the actual rules that Rust implements. You probably will experience similar things at first. There is good news, however: more experienced Rust developers report that once they work with the rules of the ownership system for a period of time, they fight the borrow checker less and less. -With that in mind, let's learn about ownership. +With that in mind, let’s learn about ownership. # Ownership -At its core, ownership is about *resources*. For the purposes of the vast -majority of this guide, we will talk about a specific resource: memory. The -concept generalizes to any kind of resource, like a file handle, but to make it -more concrete, we'll focus on memory. - -When your program allocates some memory, it needs some way to deallocate that -memory. Imagine a function `foo` that allocates four bytes of memory, and then -never deallocates that memory. We call this problem *leaking* memory, because -each time we call `foo`, we're allocating another four bytes. Eventually, with -enough calls to `foo`, we will run our system out of memory. That's no good. So -we need some way for `foo` to deallocate those four bytes. It's also important -that we don't deallocate too many times, either. Without getting into the -details, attempting to deallocate memory multiple times can lead to problems. -In other words, any time some memory is allocated, we need to make sure that we -deallocate that memory once and only once. Too many times is bad, not enough -times is bad. The counts must match. - -There's one other important detail with regards to allocating memory. Whenever -we request some amount of memory, what we are given is a handle to that memory. -This handle (often called a *pointer*, when we're referring to memory) is how -we interact with the allocated memory. As long as we have that handle, we can -do something with the memory. Once we're done with the handle, we're also done -with the memory, as we can't do anything useful without a handle to it. - -Historically, systems programming languages require you to track these -allocations, deallocations, and handles yourself. For example, if we want some -memory from the heap in a language like C, we do this: - -```c -{ - int *x = malloc(sizeof(int)); - - // we can now do stuff with our handle x - *x = 5; - - free(x); -} -``` - -The call to `malloc` allocates some memory. The call to `free` deallocates the -memory. There's also bookkeeping about allocating the correct amount of memory. - -Rust combines these two aspects of allocating memory (and other resources) into -a concept called *ownership*. Whenever we request some memory, that handle we -receive is called the *owning handle*. Whenever that handle goes out of scope, -Rust knows that you cannot do anything with the memory anymore, and so -therefore deallocates the memory for you. Here's the equivalent example in -Rust: +[`Variable bindings`][bindings] have a property in Rust: they ‘have ownership’ +of what they’re bound to. This means that when a binding goes out of scope, the +resource that they’re bound to are freed. For example: ```rust -{ - let x = Box::new(5); +fn foo() { + let v = vec![1, 2, 3]; } ``` -The `Box::new` function creates a `Box` (specifically `Box` in this -case) by allocating a small segment of memory on the heap with enough space to -fit an `i32`. But where in the code is the box deallocated? We said before that -we must have a deallocation for each allocation. Rust handles this for you. It -knows that our handle, `x`, is the owning reference to our box. Rust knows that -`x` will go out of scope at the end of the block, and so it inserts a call to -deallocate the memory at the end of the scope. Because the compiler does this -for us, it's impossible to forget. We always have exactly one deallocation - paired with each of our allocations. +When `v` comes into scope, a new [`Vec`][vect] is created. In this case, the +vector also allocates space on [the heap][heap], for the three elements. When +`v` goes out of scope at the end of `foo()`, Rust will clean up everything +related to the vector, even the heap-allocated memory. This happens +deterministically, at the end of the scope. -This is pretty straightforward, but what happens when we want to pass our box -to a function? Let's look at some code: +[vect]: ../std/vec/struct.Vec.html +[heap]: the-stack-and-the-heap.html + +# Move semantics + +There’s some more subtlety here, though: Rust ensures that there is _exactly +one_ binding to any given resource. For example, if we have a vector, we can +assign it to another binding: ```rust -fn main() { - let x = Box::new(5); +let v = vec![1, 2, 3]; - add_one(x); -} - -fn add_one(mut num: Box) { - *num += 1; -} +let v2 = v; ``` -This code works, but it's not ideal. For example, let's add one more line of -code, where we print out the value of `x`: +But, if we try to use `v` afterwards, we get an error: -```{rust,ignore} -fn main() { - let x = Box::new(5); +```rust,ignore +let v = vec![1, 2, 3]; - add_one(x); +let v2 = v; - println!("{}", x); -} - -fn add_one(mut num: Box) { - *num += 1; -} +println!("v[0] is: {}", v[0]); ``` -This does not compile, and gives us an error: +It looks like this: ```text -error: use of moved value: `x` - println!("{}", x); - ^ +error: use of moved value: `v` +println!("v[0] is: {}", v[0]); + ^ ``` -Remember, we need one deallocation for every allocation. When we try to pass -our box to `add_one`, we would have two handles to the memory: `x` in `main`, -and `num` in `add_one`. If we deallocated the memory when each handle went out -of scope, we would have two deallocations and one allocation, and that's wrong. -So when we call `add_one`, Rust defines `num` as the owner of the handle. And -so, now that we've given ownership to `num`, `x` is invalid. `x`'s value has -"moved" from `x` to `num`. Hence the error: use of moved value `x`. +A similar thing happens if we define a function which takes ownership, and +try to use something after we’ve passed it as an argument: -To fix this, we can have `add_one` give ownership back when it's done with the -box: +```rust,ignore +fn take(v: Vec) { + // what happens here isn’t important. +} + +let v = vec![1, 2, 3]; + +take(v); + +println!("v[0] is: {}", v[0]); +``` + +Same error: “use of moved value.” When we transfer ownership to something else, +we say that we’ve ‘moved’ the thing we refer to. You don’t need some sort of +special annotation here, it’s the default thing that Rust does. + +## The details + +The reason that we cannot use a binding after we’ve moved it is subtle, but +important. When we write code like this: ```rust -fn main() { - let x = Box::new(5); +let v = vec![1, 2, 3]; - let y = add_one(x); - - println!("{}", y); -} - -fn add_one(mut num: Box) -> Box { - *num += 1; - - num -} +let v2 = v; ``` -This code will compile and run just fine. Now, we return a `box`, and so the -ownership is transferred back to `y` in `main`. We only have ownership for the -duration of our function before giving it back. This pattern is very common, -and so Rust introduces a concept to describe a handle which temporarily refers -to something another handle owns. It's called *borrowing*, and it's done with -*references*, designated by the `&` symbol. +The first line creates some data for the vector on the [stack][sh], `v`. The +vector’s data, however, is stored on the [heap][sh], and so it contains a +pointer to that data. When we move `v` to `v2`, it creates a copy of that data, +for `v2`. Which would mean two pointers to the contents of the vector on the +heap. That would be a problem: it would violate Rust’s safety guarantees by +introducing a data race. Therefore, Rust forbids using `v` after we’ve done the +move. -# Borrowing +[sh]: the-stack-and-the-heap.html -Here's the current state of our `add_one` function: +It’s also important to note that optimizations may remove the actual copy of +the bytes, depending on circumstances. So it may not be as inefficient as it +initially seems. + +## `Copy` types + +We’ve established that when ownership is transferred to another binding, you +cannot use the original binding. However, there’s a [trait][traits] that changes this +behavior, and it’s called `Copy`. We haven’t discussed traits yet, but for now, +you can think of them as an annotation to a particular type that adds extra +behavior. For example: ```rust -fn add_one(mut num: Box) -> Box { - *num += 1; +let v = 1; - num -} +let v2 = v; + +println!("v is: {}", v); ``` -This function takes ownership, because it takes a `Box`, which owns its -contents. But then we give ownership right back. +In this case, `v` is an `i32`, which implements the `Copy` trait. This means +that, just like a move, when we assign `v` to `v2`, a copy of the data is made. +But, unlike a move, we can still use `v` afterward. This is because an `i32` +has no pointers to data somewhere else, copying it is a full copy. -In the physical world, you can give one of your possessions to someone for a -short period of time. You still own your possession, you're just letting someone -else use it for a while. We call that *lending* something to someone, and that -person is said to be *borrowing* that something from you. +We will discuss how to make your own types `Copy` in the [traits][traits] +section. -Rust's ownership system also allows an owner to lend out a handle for a limited -period. This is also called *borrowing*. Here's a version of `add_one` which -borrows its argument rather than taking ownership: +[traits]: traits.html + +# More than ownership + +Of course, if we had to hand ownership back with every function we wrote: ```rust -fn add_one(num: &mut i32) { - *num += 1; +fn foo(v: Vec) -> Vec { + // do stuff with v + + // hand back ownership + v } ``` -This function borrows an `i32` from its caller, and then increments it. When -the function is over, and `num` goes out of scope, the borrow is over. - -We have to change our `main` a bit too: +This would get very tedius. It gets worse the more things we want to take ownership of: ```rust -fn main() { - let mut x = 5; +fn foo(v1: Vec, v2: Vec) -> (Vec, Vec, i32) { + // do stuff with v1 and v2 - add_one(&mut x); - - println!("{}", x); + // hand back ownership, and the result of our function + (v1, v2, 42) } -fn add_one(num: &mut i32) { - *num += 1; -} +let v1 = vec![1, 2, 3]; +let v2 = vec![1, 2, 3]; + +let (v1, v2, answer) = foo(v1, v2); ``` -We don't need to assign the result of `add_one()` anymore, because it doesn't -return anything anymore. This is because we're not passing ownership back, -since we just borrow, not take ownership. +Ugh! The return type, return line, and calling the function gets way more +complicated. -# Lifetimes +Luckily, Rust offers a feature, borrowing, which helps us solve this problem. +It’s the topic of the next section! -Lending out a reference to a resource that someone else owns can be -complicated, however. For example, imagine this set of operations: -1. I acquire a handle to some kind of resource. -2. I lend you a reference to the resource. -3. I decide I'm done with the resource, and deallocate it, while you still have - your reference. -4. You decide to use the resource. -Uh oh! Your reference is pointing to an invalid resource. This is called a -*dangling pointer* or "use after free," when the resource is memory. -To fix this, we have to make sure that step four never happens after step -three. The ownership system in Rust does this through a concept called -*lifetimes*, which describe the scope that a reference is valid for. -Remember the function that borrowed an `i32`? Let's look at it again. -```rust -fn add_one(num: &mut i32) { - *num += 1; -} -``` -Rust has a feature called *lifetime elision*, which allows you to not write -lifetime annotations in certain circumstances. This is one of them. We will -cover the others later. Without eliding the lifetimes, `add_one` looks like -this: -```rust -fn add_one<'a>(num: &'a mut i32) { - *num += 1; -} -``` -The `'a` is called a *lifetime*. Most lifetimes are used in places where -short names like `'a`, `'b` and `'c` are clearest, but it's often useful to -have more descriptive names. Let's dig into the syntax in a bit more detail: -```{rust,ignore} -fn add_one<'a>(...) -``` -This part _declares_ our lifetimes. This says that `add_one` has one lifetime, -`'a`. If we had two, it would look like this: - -```{rust,ignore} -fn add_two<'a, 'b>(...) -``` - -Then in our parameter list, we use the lifetimes we've named: - -```{rust,ignore} -...(num: &'a mut i32) -``` - -If you compare `&mut i32` to `&'a mut i32`, they're the same, it's just that the -lifetime `'a` has snuck in between the `&` and the `mut i32`. We read `&mut i32` as "a -mutable reference to an i32" and `&'a mut i32` as "a mutable reference to an i32 with the lifetime 'a.'" - -Why do lifetimes matter? Well, for example, here's some code: - -```rust -struct Foo<'a> { - x: &'a i32, -} - -fn main() { - let y = &5; // this is the same as `let _y = 5; let y = &_y;` - let f = Foo { x: y }; - - println!("{}", f.x); -} -``` - -As you can see, `struct`s can also have lifetimes. In a similar way to functions, - -```{rust} -struct Foo<'a> { -# x: &'a i32, -# } -``` - -declares a lifetime, and - -```rust -# struct Foo<'a> { -x: &'a i32, -# } -``` - -uses it. So why do we need a lifetime here? We need to ensure that any reference -to a `Foo` cannot outlive the reference to an `i32` it contains. - -## Thinking in scopes - -A way to think about lifetimes is to visualize the scope that a reference is -valid for. For example: - -```rust -fn main() { - let y = &5; // -+ y goes into scope - // | - // stuff // | - // | -} // -+ y goes out of scope -``` - -Adding in our `Foo`: - -```rust -struct Foo<'a> { - x: &'a i32, -} - -fn main() { - let y = &5; // -+ y goes into scope - let f = Foo { x: y }; // -+ f goes into scope - // stuff // | - // | -} // -+ f and y go out of scope -``` - -Our `f` lives within the scope of `y`, so everything works. What if it didn't? -This code won't work: - -```{rust,ignore} -struct Foo<'a> { - x: &'a i32, -} - -fn main() { - let x; // -+ x goes into scope - // | - { // | - let y = &5; // ---+ y goes into scope - let f = Foo { x: y }; // ---+ f goes into scope - x = &f.x; // | | error here - } // ---+ f and y go out of scope - // | - println!("{}", x); // | -} // -+ x goes out of scope -``` - -Whew! As you can see here, the scopes of `f` and `y` are smaller than the scope -of `x`. But when we do `x = &f.x`, we make `x` a reference to something that's -about to go out of scope. - -Named lifetimes are a way of giving these scopes a name. Giving something a -name is the first step towards being able to talk about it. - -## 'static - -The lifetime named *static* is a special lifetime. It signals that something -has the lifetime of the entire program. Most Rust programmers first come across -`'static` when dealing with strings: - -```rust -let x: &'static str = "Hello, world."; -``` - -String literals have the type `&'static str` because the reference is always -alive: they are baked into the data segment of the final binary. Another -example are globals: - -```rust -static FOO: i32 = 5; -let x: &'static i32 = &FOO; -``` - -This adds an `i32` to the data segment of the binary, and `x` is a reference -to it. - -# Shared Ownership - -In all the examples we've considered so far, we've assumed that each handle has -a singular owner. But sometimes, this doesn't work. Consider a car. Cars have -four wheels. We would want a wheel to know which car it was attached to. But -this won't work: - -```{rust,ignore} -struct Car { - name: String, -} - -struct Wheel { - size: i32, - owner: Car, -} - -fn main() { - let car = Car { name: "DeLorean".to_string() }; - - for _ in 0..4 { - Wheel { size: 360, owner: car }; - } -} -``` - -We try to make four `Wheel`s, each with a `Car` that it's attached to. But the -compiler knows that on the second iteration of the loop, there's a problem: - -```text -error: use of moved value: `car` - Wheel { size: 360, owner: car }; - ^~~ -note: `car` moved here because it has type `Car`, which is non-copyable - Wheel { size: 360, owner: car }; - ^~~ -``` - -We need our `Car` to be pointed to by multiple `Wheel`s. We can't do that with -`Box`, because it has a single owner. We can do it with `Rc` instead: - -```rust -use std::rc::Rc; - -struct Car { - name: String, -} - -struct Wheel { - size: i32, - owner: Rc, -} - -fn main() { - let car = Car { name: "DeLorean".to_string() }; - - let car_owner = Rc::new(car); - - for _ in 0..4 { - Wheel { size: 360, owner: car_owner.clone() }; - } -} -``` - -We wrap our `Car` in an `Rc`, getting an `Rc`, and then use the -`clone()` method to make new references. We've also changed our `Wheel` to have -an `Rc` rather than just a `Car`. - -This is the simplest kind of multiple ownership possible. For example, there's -also `Arc`, which uses more expensive atomic instructions to be the -thread-safe counterpart of `Rc`. - -## Lifetime Elision - -Rust supports powerful local type inference in function bodies, but it’s -forbidden in item signatures to allow reasoning about the types just based in -the item signature alone. However, for ergonomic reasons a very restricted -secondary inference algorithm called “lifetime elision” applies in function -signatures. It infers only based on the signature components themselves and not -based on the body of the function, only infers lifetime parameters, and does -this with only three easily memorizable and unambiguous rules. This makes -lifetime elision a shorthand for writing an item signature, while not hiding -away the actual types involved as full local inference would if applied to it. - -When talking about lifetime elision, we use the term *input lifetime* and -*output lifetime*. An *input lifetime* is a lifetime associated with a parameter -of a function, and an *output lifetime* is a lifetime associated with the return -value of a function. For example, this function has an input lifetime: - -```{rust,ignore} -fn foo<'a>(bar: &'a str) -``` - -This one has an output lifetime: - -```{rust,ignore} -fn foo<'a>() -> &'a str -``` - -This one has a lifetime in both positions: - -```{rust,ignore} -fn foo<'a>(bar: &'a str) -> &'a str -``` - -Here are the three rules: - -* Each elided lifetime in a function's arguments becomes a distinct lifetime - parameter. - -* If there is exactly one input lifetime, elided or not, that lifetime is - assigned to all elided lifetimes in the return values of that function. - -* If there are multiple input lifetimes, but one of them is `&self` or `&mut - self`, the lifetime of `self` is assigned to all elided output lifetimes. - -Otherwise, it is an error to elide an output lifetime. - -### Examples - -Here are some examples of functions with elided lifetimes. We've paired each -example of an elided lifetime with its expanded form. - -```{rust,ignore} -fn print(s: &str); // elided -fn print<'a>(s: &'a str); // expanded - -fn debug(lvl: u32, s: &str); // elided -fn debug<'a>(lvl: u32, s: &'a str); // expanded - -// In the preceding example, `lvl` doesn't need a lifetime because it's not a -// reference (`&`). Only things relating to references (such as a `struct` -// which contains a reference) need lifetimes. - -fn substr(s: &str, until: u32) -> &str; // elided -fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded - -fn get_str() -> &str; // ILLEGAL, no inputs - -fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs -fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is unclear - -fn get_mut(&mut self) -> &mut T; // elided -fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded - -fn args(&mut self, args: &[T]) -> &mut Command // elided -fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded - -fn new(buf: &mut [u8]) -> BufWriter; // elided -fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded -``` - -# Related Resources - -Coming Soon. diff --git a/src/doc/trpl/references-and-borrowing.md b/src/doc/trpl/references-and-borrowing.md index 0e13ea612645..21feff73342c 100644 --- a/src/doc/trpl/references-and-borrowing.md +++ b/src/doc/trpl/references-and-borrowing.md @@ -1,3 +1,336 @@ % References and Borrowing -Coming Soon! Until then, check out the [ownership](ownership.html) chapter. +This guide is one of three presenting Rust’s ownership system. This is one of +Rust’s most unique and compelling features, with which Rust developers should +become quite acquainted. Ownership is how Rust achieves its largest goal, +memory safety. The there are a few distinct concepts, each with its own +chapter: + +* [ownership][ownership], ownership, the key concept +* borrowing, which you’re reading now +* [lifetimes][lifetimes], an advanced concept of borrowing + +These three chapters are related, and in order. You’ll need all three to fully +understand the ownership system. + +[ownership]: ownership.html +[lifetimes]: lifetimes.html + +# Meta + +Before we get to the details, two important notes about the ownership system. + +Rust has a focus on safety and speed. It accomplishes these goals through many +‘zero-cost abstractions’, which means that in Rust, abstractions cost as little +as possible in order to make them work. The ownership system is a prime example +of a zero cost abstraction. All of the analysis we’ll talk about in this guide +is _done at compile time_. You do not pay any run-time cost for any of these +features. + +However, this system does have a certain cost: learning curve. Many new users +to Rust experience something we like to call ‘fighting with the borrow +checker’, where the Rust compiler refuses to compile a program that the author +thinks is valid. This often happens because the programmer’s mental model of +how ownership should work doesn’t match the actual rules that Rust implements. +You probably will experience similar things at first. There is good news, +however: more experienced Rust developers report that once they work with the +rules of the ownership system for a period of time, they fight the borrow +checker less and less. + +With that in mind, let’s learn about borrowing. + +# Borrowing + +At the end of the [ownership][ownership] section, we had a nasty function that looked +like this: + +```rust +fn foo(v1: Vec, v2: Vec) -> (Vec, Vec, i32) { + // do stuff with v1 and v2 + + // hand back ownership, and the result of our function + (v1, v2, 42) +} + +let v1 = vec![1, 2, 3]; +let v2 = vec![1, 2, 3]; + +let (v1, v2, answer) = foo(v1, v2); +``` + +This is not idiomatic Rust, however, as it doesn’t take advantage of borrowing. Here’s +the first step: + +```rust +fn foo(v1: &Vec, v2: &Vec) -> i32 { + // do stuff with v1 and v2 + + // return the answer + 42 +} + +let v1 = vec![1, 2, 3]; +let v2 = vec![1, 2, 3]; + +let answer = foo(&v1, &v2); + +// we can use v1 and v2 here! +``` + +Instead of taking `Vec`s as our arguments, we take a reference: +`&Vec`. And instead of passing `v1` and `v2` directly, we pass `&v1` and +`&v2`. We call the `&T` type a ‘reference’, and rather than owning the resource, +it borrows ownership. A binding that borrows something does not deallocate the +resource when it goes out of scope. This means that after the call to `foo()`, +we can use our original bindings again. + +References are immutable, just like bindings. This means that inside of `foo()`, +the vectors can’t be changed at all: + +```rust,ignore +fn foo(v: &Vec) { + v.push(5); +} + +let v = vec![]; + +foo(&v); +``` + +errors with: + +```text +error: cannot borrow immutable borrowed content `*v` as mutable +v.push(5); +^ +``` + +Pushing a value mutates the vector, and so we aren’t allowed to do it. + +# &mut references + +There’s a second kind of reference: `&mut T`. A ‘mutable reference’ allows you +to mutate the resource you’re borrowing. For example: + +```rust +let mut x = 5; +{ + let y = &mut x; + *y += 1; +} +println!("{}", x); +``` + +This will print `6`. We make `y` a mutable reference to `x`, then add one to +the thing `y` points at. You’ll notice that `x` had to be marked `mut` as well, +if it wasn’t, we couldn’t take a mutable borrow to an immutable value. + +Otherwise, `&mut` references are just like references. There _is_ a large +difference between the two, and how they interact, though. You can tell +something is fishy in the above example, because we need that extra scope, with +the `{` and `}`. If we remove them, we get an error: + +```text +error: cannot borrow `x` as immutable because it is also borrowed as mutable + println!("{}", x); + ^ +note: previous borrow of `x` occurs here; the mutable borrow prevents +subsequent moves, borrows, or modification of `x` until the borrow ends + let y = &mut x; + ^ +note: previous borrow ends here +fn main() { + +} +^ +``` + +As it turns out, there are rules. + +# The Rules + +Here’s the rules about borrowing in Rust: + +First, any borrow must last for a smaller scope than the owner. Second, you may +have one or the other of these two kinds of borrows, but not both at the same +time: + +* 0 to N references (`&T`) to a resource. +* exactly one mutable reference (`&mut T`) + + +You may notice that this is very similar, though not exactly the same as, +to the definition of a data race: + +> There is a ‘data race’ when two or more pointers access the same memory +> location at the same time, where at least one of them is writing, and the +> operations are not synchronized. + +With references, you may have as many as you’d like, since none of them are +writing. If you are writing, you need two or more pointers to the same memory, +and you can only have one `&mut` at a time. This is how Rust prevents data +races at compile time: we’ll get errors if we break the rules. + +With this in mind, let’s consider our example again. + +## Thinking in scopes + +Here’s the code: + +```rust,ignore +let mut x = 5; +let y = &mut x; + +*y += 1; + +println!("{}", x); +``` + +This code gives us this error: + +```text +error: cannot borrow `x` as immutable because it is also borrowed as mutable + println!("{}", x); + ^ +``` + +This is because we’ve violated the rules: we have a `&mut T` pointing to `x`, +and so we aren’t allowed to create any `&T`s. One or the other. The note +hints at how to think about this problem: + +```text +note: previous borrow ends here +fn main() { + +} +^ +``` + +In other words, the mutable borow is held through the rest of our example. What +we want is for the mutable borrow to end _before_ we try to call `println!` and +make an immutable borrow. In Rust, borrowing is tied to the scope that the +borrow is valid for. And our scopes look like this: + +```rust,ignore +let mut x = 5; + +let y = &mut x; // -+ &mut borrow of x starts here + // | +*y += 1; // | + // | +println!("{}", x); // -+ - try to borrow x here + // -+ &mut borrow of x ends here +``` + +The scopes conflict: we can’t make an `&x` while `y` is in scope. + +So when we add the curly braces: + +```rust +let mut x = 5; + +{ + let y = &mut x; // -+ &mut borrow starts here + *y += 1; // | +} // -+ ... and ends here + +println!("{}", x); // <- try to borrow x here +``` + +There’s no problem. Our mutable borrow goes out of scope before we create an +immutable one. But scope is the key to seeing how long a borrow lasts for. + +## Issues borrowing prevents + +Why have these restrictive rules? Well, as we noted, these rules prevent data +races. What kinds of issues do data races cause? Here’s a few. + +### Iterator invalidation + +One example is ‘iterator invalidation’, which happens when you try to mutate a +collection that you’re iterating over. Rust’s borrow checker prevents this from +happening: + +```rust +let mut v = vec![1, 2, 3]; + +for i in &v { + println!("{}", i); +} +``` + +This prints out one through three. As we iterate through the vectors, we’re +only given references to the elements. And `v` is itself borrowed as immutable, +which means we can’t change it while we’re iterating: + +```rust,ignore +let mut v = vec![1, 2, 3]; + +for i in &v { + println!("{}", i); + v.push(34); +} +``` + +Here’s the error: + +```text +error: cannot borrow `v` as mutable because it is also borrowed as immutable + v.push(34); + ^ +note: previous borrow of `v` occurs here; the immutable borrow prevents +subsequent moves or mutable borrows of `v` until the borrow ends +for i in &v { + ^ +note: previous borrow ends here +for i in &v { + println!(“{}”, i); + v.push(34); +} +^ +``` + +We can’t modify `v` because it’s borrowed by the loop. + +### use after free + +References must live as long as the resource they refer to. Rust will check the +scopes of your references to ensure that this is true. + +If Rust didn’t check that this property, we could accidentally use a reference +which was invalid. For example: + +```rust,ignore +let y: &i32; +{ + let x = 5; + y = &x; +} + +println!("{}", y); +``` + +We get this error: + +error: `x` does not live long enough + y = &x; + ^ +note: reference must be valid for the block suffix following statement 0 at +2:16... +let y: &i32; +{ + let x = 5; + y = &x; +} + +note: ...but borrowed value is only valid for the block suffix following +statement 0 at 4:18 + let x = 5; + y = &x; +} +``` + +In other words, `y` is only valid for the scope where `x` exists. As soon as +`x` goes away, it becomes invalid to refer to it. As such, the error says that +the borrow ‘doesn’t live long enough’ because it’s not valid for the right +amount of time. From 2741b94daaa56c0f88ed91a7a4e9b888a5b835a0 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Tue, 5 May 2015 16:13:48 -0400 Subject: [PATCH 4/5] Indicate code is code-like in diagnostic error message --- src/librustc/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 6cb39c39659e..0aefba28efa6 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -37,7 +37,7 @@ An example of an empty type is `enum Empty { }`. E0003: r##" Not-a-Number (NaN) values cannot be compared for equality and hence can never match the input to a match expression. To match against NaN values, you should -instead use the `is_nan` method in a guard, as in: x if x.is_nan() => ... +instead use the `is_nan` method in a guard, as in: `x if x.is_nan() => ...` "##, E0004: r##" From 9d512fdd196413aa6164a686ed5641c31270d632 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sun, 3 May 2015 13:09:49 -0400 Subject: [PATCH 5/5] 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 | 999 ++++++++++++++++++++++++++++++++++ src/doc/trpl/learn-rust.md | 9 +- src/doc/trpl/method-syntax.md | 7 +- 4 files changed, 1011 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 695dc42cb641..fb04b323cb05 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 000000000000..431b7dc50a29 --- /dev/null +++ b/src/doc/trpl/guessing-game.md @@ -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 "] +``` + +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 '
' 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. diff --git a/src/doc/trpl/learn-rust.md b/src/doc/trpl/learn-rust.md index 3d8ef8090bfb..1a02bc95e9d9 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 1445d39fe873..ed4e9dd359b6 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