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 @@ 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 @@ 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 @@ } ``` -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 @@ struct X { 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 @@ struct X { 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 @@ enum Enum { 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 @@ fn main() { 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 @@ fn main() { 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 @@ enum Method { ``` 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 @@ enum Method { GET, POST } "##, 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 @@ enum Method { GET, POST } } ``` -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 @@ 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