Auto merge of #25129 - steveklabnik:rollup, r=steveklabnik
- Successful merges: #24782, #25080, #25112, #25114, #25127 - Failed merges:
This commit is contained in:
commit
252b5444da
10 changed files with 1794 additions and 611 deletions
|
|
@ -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)
|
||||
|
|
@ -26,7 +27,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)
|
||||
|
|
|
|||
999
src/doc/trpl/guessing-game.md
Normal file
999
src/doc/trpl/guessing-game.md
Normal file
|
|
@ -0,0 +1,999 @@
|
|||
% Guessing Game
|
||||
|
||||
For our first project, we’ll implement a classic beginner programming problem:
|
||||
the guessing game. Here’s how it works: Our program will generate a random
|
||||
integer between one and a hundred. It will then prompt us to enter a guess.
|
||||
Upon entering our guess, it will tell us if we’re too low or too high. Once we
|
||||
guess correctly, it will congratulate us. Sounds good?
|
||||
|
||||
# Set up
|
||||
|
||||
Let’s set up a new project. Go to your projects directory. Remember how we had
|
||||
to create our directory structure and a `Cargo.toml` for `hello_world`? Cargo
|
||||
has a command that does that for us. Let’s give it a shot:
|
||||
|
||||
```bash
|
||||
$ cd ~/projects
|
||||
$ cargo new guessing_game --bin
|
||||
$ cd guessing_game
|
||||
```
|
||||
|
||||
We pass the name of our project to `cargo new`, and then the `--bin` flag,
|
||||
since we’re making a binary, rather than a library.
|
||||
|
||||
Check out the generated `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
|
||||
name = "guessing_game"
|
||||
version = "0.0.1"
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
```
|
||||
|
||||
Cargo gets this information from your environment. If it’s not correct, go ahead
|
||||
and fix that.
|
||||
|
||||
Finally, Cargo generated a ‘Hello, world!’ for us. Check out `src/main.rs`:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!")
|
||||
}
|
||||
```
|
||||
|
||||
Let’s try compiling what Cargo gave us:
|
||||
|
||||
```{bash}
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
```
|
||||
|
||||
Excellent! Open up your `src/main.rs` again. We’ll be writing all of
|
||||
our code in this file.
|
||||
|
||||
Before we move on, let me show you one more Cargo command: `run`. `cargo run`
|
||||
is kind of like `cargo build`, but it also then runs the produced executable.
|
||||
Try it out:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/debug/guessing_game`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Great! The `run` command comes in handy when you need to rapidly iterate on a
|
||||
project. Our game is just such a project, we need to quickly test each
|
||||
iteration before moving on to the next one.
|
||||
|
||||
# Processing a Guess
|
||||
|
||||
Let’s get to it! The first thing we need to do for our guessing game is
|
||||
allow our player to input a guess. Put this in your `src/main.rs`:
|
||||
|
||||
```rust,no_run
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
let input = io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
There’s a lot here! Let’s go over it, bit by bit.
|
||||
|
||||
```rust,ignore
|
||||
use std::io;
|
||||
```
|
||||
|
||||
We’ll need to take user input, and then print the result as output. As such, we
|
||||
need the `io` library from the standard library. Rust only imports a few things
|
||||
into every program, [the ‘prelude’][prelude]. If it’s not in the prelude,
|
||||
you’ll have to `use` it directly.
|
||||
|
||||
[prelude]: ../std/prelude/index.html
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
```
|
||||
|
||||
As you’ve seen before, the `main()` function is the entry point into your
|
||||
program. The `fn` syntax declares a new function, the `()`s indicate that
|
||||
there are no arguments, and `{` starts the body of the function. Because
|
||||
we didn’t include a return type, it’s assumed to be `()`, an empty
|
||||
[tuple][tuples].
|
||||
|
||||
[tuples]: primitive-types.html#tuples
|
||||
|
||||
```rust,ignore
|
||||
println!("Guess the number!");
|
||||
|
||||
println!("Please input your guess.");
|
||||
```
|
||||
|
||||
We previously learned that `println!()` is a [macro][macros] that
|
||||
prints a [string][strings] to the screen.
|
||||
|
||||
[macros]: macros.html
|
||||
[strings]: strings.html
|
||||
|
||||
```rust,ignore
|
||||
let mut guess = String::new();
|
||||
```
|
||||
|
||||
Now we’re getting interesting! There’s a lot going on in this little line. The first thing to notice is that this is a [let statement][let], which is used to create ‘variable bindings’. They take this form:
|
||||
|
||||
```rust,ignore
|
||||
let foo = bar;
|
||||
```
|
||||
|
||||
[let]: variable-bindings.html
|
||||
|
||||
This will create a new binding named `foo`, and bind it to the value `bar`. In
|
||||
many languages, this is called a ‘variable’, but Rust’s variable bindings have
|
||||
a few tricks up their sleeves.
|
||||
|
||||
For example, they’re [immutable][immutable] by default. That’s why our example
|
||||
uses `mut`: it makes a binding mutable, rather than immutable. `let` doesn’t
|
||||
take a name on the left hand side, it actually accepts a
|
||||
‘[pattern][patterns]’. We’ll use patterns more later. It’s easy enough
|
||||
to use for now:
|
||||
|
||||
```
|
||||
let foo = 5; // immutable.
|
||||
let mut bar = 5; // mutable
|
||||
```
|
||||
|
||||
[immutable]: mutability.html
|
||||
[patterns]: patterns.html
|
||||
|
||||
Oh, and `//` will start a comment, until the end of the line. Rust ignores
|
||||
everything in [comments][comments].
|
||||
|
||||
[comments]: comments.html
|
||||
|
||||
So now we know that `let mut guess` will introduce a mutable binding named
|
||||
`guess`, but we have to look at the other side of the `=` for what it’s
|
||||
bound to: `String::new()`.
|
||||
|
||||
`String` is a string type, provided by the standard library. A
|
||||
[`String`][string] is a growable, UTF-8 encoded bit of text.
|
||||
|
||||
[string]: ../std/string/struct.String.html
|
||||
|
||||
The `::new()` syntax is uses `::` because this is an ‘associated function’ of
|
||||
a particular type. That is to say, it’s associated with `String` itself,
|
||||
rather than a particular instance of a `String`. Some languages call this a
|
||||
‘static method’.
|
||||
|
||||
This function is named `new()`, because it creates a new, empty `String`.
|
||||
You’ll find a `new()` function on many types, as it’s a common name for making
|
||||
a new value of some kind.
|
||||
|
||||
Let’s move forward:
|
||||
|
||||
```rust,ignore
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
```
|
||||
|
||||
That’s a lot more! Let’s go bit-by-bit. The first line has two parts. Here’s
|
||||
the first:
|
||||
|
||||
```rust,ignore
|
||||
io::stdin()
|
||||
```
|
||||
|
||||
Remember how we `use`d `std::io` on the first line of the program? We’re now
|
||||
calling an associated function on it. If we didn’t `use std::io`, we could
|
||||
have written this line as `std::io::stdin()`.
|
||||
|
||||
This particular function returns a handle to the standard input for your
|
||||
terminal. More specifically, a [std::io::Stdin][iostdin].
|
||||
|
||||
[iostdin]: ../std/io/struct.Stdin.html
|
||||
|
||||
The next part will use this handle to get input from the user:
|
||||
|
||||
```rust,ignore
|
||||
.read_line(&mut guess)
|
||||
```
|
||||
|
||||
Here, we call the [`read_line()`][read_line] method on our handle.
|
||||
[Method][method]s are like associated functions, but are only available on a
|
||||
particular instance of a type, rather than the type itself. We’re also passing
|
||||
one argument to `read_line()`: `&mut guess`.
|
||||
|
||||
[read_line]: ../std/io/struct.Stdin.html#method.read_line
|
||||
[method]: methods.html
|
||||
|
||||
Remember how we bound `guess` above? We said it was mutable. However,
|
||||
`read_line` doesn’t take a `String` as an argument: it takes a `&mut String`.
|
||||
Rust has a feature called ‘[references][references]’, which allows you to have
|
||||
multiple references to one piece of data, which can reduce copying. References
|
||||
are a complex feature, as one of Rust’s major selling points is how safe and
|
||||
easy it is to use references. We don’t need to know a lot of those details to
|
||||
finish our program right now, though. For now, all we need to know is that
|
||||
like `let` bindings, references are immutable by default. Hence, we need to
|
||||
write `&mut guess`, rather than `&guess`.
|
||||
|
||||
Why does `read_line()` take a mutable reference to a string? Its job is
|
||||
to take what the user types into standard input, and place that into a
|
||||
string. So it takes that string as an argument, and in order to add
|
||||
the input, it needs to be mutable.
|
||||
|
||||
[references]: references-and-borrowing.html
|
||||
|
||||
But we’re not quite done with this line of code, though. While it’s
|
||||
a single line of text, it’s only the first part of the single logical line of
|
||||
code:
|
||||
|
||||
```rust,ignore
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
```
|
||||
|
||||
When you call a method with the `.foo()` syntax, you may introduce a newline
|
||||
and other whitespace. This helps you split up long lines. We _could_ have
|
||||
done:
|
||||
|
||||
```rust,ignore
|
||||
io::stdin().read_line(&mut guess).ok().expect("failed to read line");
|
||||
```
|
||||
|
||||
But that gets hard to read. So we’ve split it up, three lines for three
|
||||
method calls. We already talked about `read_line()`, but what about `ok()`
|
||||
and `expect()`? Well, we already mentioned that `read_line()` puts what
|
||||
the user types into the `&mut String` we pass it. But it also returns
|
||||
a value: in this case, an [`io::Result`][ioresult]. Rust has a number of
|
||||
types named `Result` in its standard library: a generic [`Result`][result],
|
||||
and then specific versions for sub-libraries, like `io::Result`.
|
||||
|
||||
[ioresult]: ../std/io/type.Result.html
|
||||
[result]: ../std/result/enum.Result.html
|
||||
|
||||
The purpose of these `Result` types is to encode error handling information.
|
||||
Values of the `Result` type, like any type, have methods defined on them. In
|
||||
this case, `io::Result` has an `ok()` method, which says ‘we want to assume
|
||||
this value is a successful one. If not, just throw away the error
|
||||
information’. Why throw it away? Well, for a basic program, we just want to
|
||||
print a generic error, as basically any issue means we can’t continue. The
|
||||
[`ok()` method][ok] returns a value which has another method defined on it:
|
||||
`expect()`. The [`expect()` method][expect] takes a value it’s called on, and
|
||||
if it isn’t a successful one, [`panic!`][panic]s with a message you passed you
|
||||
passed it. A `panic!` like this will cause our program to crash, displaying
|
||||
the message.
|
||||
|
||||
[ok]: ../std/result/enum.Result.html#method.ok
|
||||
[expect]: ../std/option/enum.Option.html#method.expect
|
||||
[panic]: error-handling.html
|
||||
|
||||
If we leave off calling these two methods, our program will compile, but
|
||||
we’ll get a warning:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:10:5: 10:39 warning: unused result which must be used,
|
||||
#[warn(unused_must_use)] on by default
|
||||
src/main.rs:10 io::stdin().read_line(&mut guess);
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
Rust warns us that we haven’t used the `Result` value. This warning comes from
|
||||
a special annotation that `io::Result` has. Rust is trying to tell you that
|
||||
you haven’t handled a possible error. The right way to suppress the error is
|
||||
to actually write error handling. Luckily, if we just want to crash if there’s
|
||||
a problem, we can use these two little methods. If we can recover from the
|
||||
error somehow, we’d do something else, but we’ll save that for a future
|
||||
project.
|
||||
|
||||
There’s just one line of this first example left:
|
||||
|
||||
```rust,ignore
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
This prints out the string we saved our input in. The `{}`s are a placeholder,
|
||||
and so we pass it `input` as an argument. If we had multiple `{}`s, we would
|
||||
pass multiple arguments:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
println!("x and y: {} and {}", x, y);
|
||||
```
|
||||
|
||||
Easy.
|
||||
|
||||
Anyway, that’s the tour. We can run what we have with `cargo run`:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
Please input your guess.
|
||||
6
|
||||
You guessed: 6
|
||||
```
|
||||
|
||||
All right! Our first part is done: we can get input from the keyboard,
|
||||
and then print it back out.
|
||||
|
||||
# Generating a secret number
|
||||
|
||||
Next, we need to generate a secret number. Rust does not yet include random
|
||||
number functionality in its standard library. The Rust team does, however,
|
||||
provide a [`rand` crate][randcrate]. A ‘crate’ is a package of Rust code.
|
||||
We’ve been building a ‘binary crate’, which is an executable. `rand` is a
|
||||
‘library crate’, which contains code that’s intended to be used with other
|
||||
programs.
|
||||
|
||||
[randcrate]: https://crates.io/crates/rand
|
||||
|
||||
Using external crates is where Cargo really shines. Before we can write
|
||||
the code using `rand`, we need to modify our `Cargo.toml`. Open it up, and
|
||||
add these few lines at the bottom:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
|
||||
rand="0.3.0"
|
||||
```
|
||||
|
||||
The `[dependencies]` section of `Cargo.toml` is like the `[package]` section:
|
||||
everything that follows it is part of it, until the next section starts.
|
||||
Cargo uses the dependencies section to know what dependencies on external
|
||||
crates you have, and what versions you require. In this case, we’ve used `*`,
|
||||
which means that we’ll use the latest version of `rand`. Cargo understands
|
||||
[Semantic Versioning][semver], which is a standard for writing version
|
||||
numbers. If we wanted a specific version or range of versions, we could be
|
||||
more specific here. [Cargo’s documentation][cargodoc] contains more details.
|
||||
|
||||
[semver]: http://semver.org
|
||||
[cargodoc]: http://doc.crates.io/crates-io.html
|
||||
|
||||
Now, without changing any of our code, let’s build our project:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||
Downloading rand v0.3.8
|
||||
Downloading libc v0.1.6
|
||||
Compiling libc v0.1.6
|
||||
Compiling rand v0.3.8
|
||||
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
|
||||
```
|
||||
|
||||
(You may see different versions, of course.)
|
||||
|
||||
Lots of new output! Now that we have an external dependency, Cargo fetches the
|
||||
latest versions of everything from the registry, which is a copy of data from
|
||||
[Crates.io][cratesio]. Crates.io is where people in the Rust ecosystem
|
||||
post their open source Rust projects for others to use.
|
||||
|
||||
[cratesio]: https://crates.io
|
||||
|
||||
After updating the registry, Cargo checks our `[dependencies]` and downloads
|
||||
any we don’t have yet. In this case, while we only said we wanted to depend on
|
||||
`rand`, we’ve also grabbed a copy of `libc`. This is because `rand` depends on
|
||||
`libc` to work. After downloading them, it compiles them, and then compiles
|
||||
our project.
|
||||
|
||||
If we run `cargo build` again, we’ll get different output:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
```
|
||||
|
||||
That’s right, no output! Cargo knows that our project has been built, and that
|
||||
all of its dependencies are built, and so there’s no reason to do all that
|
||||
stuff. With nothing to do, it simply exits. If we open up `src/main.rs` again,
|
||||
make a trivial change, and then save it again, we’ll just see one line:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
|
||||
```
|
||||
|
||||
So, we told Cargo we wanted any version of `rand`, and so it fetched the
|
||||
latest version at the time this was written, `v0.3.8`. But what happens
|
||||
when next week, version `v0.4.0` comes out, which changes something with
|
||||
`rand`, and it includes a breaking change? After all, a `v0.y.z` version
|
||||
in SemVer can change every release.
|
||||
|
||||
The answer to this problem is the `Cargo.lock` file you’ll now find in your
|
||||
project directory. When you build your project for the first time, Cargo
|
||||
figures out all of the versions that fit your criteria, and then writes them
|
||||
to the `Cargo.lock` file. When you build your project in the future, Cargo
|
||||
will see that the `Cargo.lock` file exists, and then use that specific version
|
||||
rather than do all the work of figuring out versions again. This lets you
|
||||
have a repeatable build automatically.
|
||||
|
||||
What about when we _do_ want to use `v0.4.0`? Cargo has another command,
|
||||
`update`, which says ‘ignore the lock, figure out all the latest versions that
|
||||
fit what we’ve specified. If that works, write those versions out to the lock
|
||||
file’.
|
||||
|
||||
There’s a lot more to say about [Cargo][doccargo] and [its
|
||||
ecosystem][doccratesio], but for now, that’s all we need to know. Cargo makes
|
||||
it really easy to re-use libraries, and so Rustaceans tend to write smaller
|
||||
projects which are assembled out of a number of sub-packages.
|
||||
|
||||
[doccargo]: http://doc.crates.io
|
||||
[doccratesio]: http://doc.crates.io/crates-io.html
|
||||
|
||||
Let’s get on to actually _using_ `rand`. Here’s our next step:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
}
|
||||
```
|
||||
|
||||
The first thing we’ve done is change the first line. It now says
|
||||
`extern crate rand`. Because we declared `rand` in our `[dependencies]`, we
|
||||
can use `extern crate` to let Rust know we’ll be making use of it. This also
|
||||
does the equivalent of a `use rand;` as well, so we can make use of anything
|
||||
in the `rand` crate by prefixing it with `rand::`.
|
||||
|
||||
Next, we added another `use` line: `use rand::Rng`. We’re going to use a
|
||||
method in a moment, and it requires that `Rng` be in scope to work. The basic
|
||||
idea is this: methods are defined on something called ‘traits’, and for the
|
||||
method to work, it needs the trait to be in scope. For more about the
|
||||
details, read the [traits][traits] section.
|
||||
|
||||
[traits]: traits.html
|
||||
|
||||
There are two other lines we added, in the middle:
|
||||
|
||||
```rust,ignore
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
```
|
||||
|
||||
We use the `rand::thread_rng()` function to get a copy of the random number
|
||||
generator, which is local to the particular [thread][concurrency] of execution
|
||||
we’re in. Because we `use rand::Rng`’d above, it has a `gen_range()` method
|
||||
available. This method takes two arguments, and generates a number between
|
||||
them. It’s inclusive on the lower bound, but exclusive on the upper bound,
|
||||
so we need `1` and `101` to get a number between one and a hundred.
|
||||
|
||||
[concurrency]: concurrency.html
|
||||
|
||||
The second line just prints out the secret number. This is useful while
|
||||
we’re developing our program, so we can easily test it out. But we’ll be
|
||||
deleting it for the final version. It’s not much of a game if it prints out
|
||||
the answer when you start it up!
|
||||
|
||||
Try running our new program a few times:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 7
|
||||
Please input your guess.
|
||||
4
|
||||
You guessed: 4
|
||||
$ cargo run
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 83
|
||||
Please input your guess.
|
||||
5
|
||||
You guessed: 5
|
||||
```
|
||||
|
||||
Great! Next up: let’s compare our guess to the secret guess.
|
||||
|
||||
# Comparing guesses
|
||||
|
||||
Now that we’ve got user input, let’s compare our guess to the random guess.
|
||||
Here’s our next step, though it doesn’t quite work yet:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use std::cmp::Ordering;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A few new bits here. The first is another `use`. We bring a type called
|
||||
`std::cmp::Ordering` into scope. Then, five new lines at the bottom that use
|
||||
it:
|
||||
|
||||
```rust,ignore
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
```
|
||||
|
||||
The `cmp()` method can be called on anything that can be compared, and it
|
||||
takes a reference to the thing you want to compare it to. It returns the
|
||||
`Ordering` type we `use`d earlier. We use a [`match`][match] statement to
|
||||
determine exactly what kind of `Ordering` it is. `Ordering` is an
|
||||
[`enum`][enum], short for ‘enumeration’, which looks like this:
|
||||
|
||||
```rust
|
||||
enum Foo {
|
||||
Bar,
|
||||
Baz,
|
||||
}
|
||||
```
|
||||
|
||||
[match]: match.html
|
||||
[enum]: enums.html
|
||||
|
||||
With this definition, anything of type `Foo` can be either a
|
||||
`Foo::Bar` or a `Foo::Baz`. We use the `::` to indicate the
|
||||
namespace for a particular `enum` variant.
|
||||
|
||||
The [`Ordering`][ordering] enum has three possible variants: `Less`, `Equal`,
|
||||
and `Greater`. The `match` statement takes a value of a type, and lets you
|
||||
create an ‘arm’ for each possible value. Since we have three types of
|
||||
`Ordering`, we have three arms:
|
||||
|
||||
```rust,ignore
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
```
|
||||
|
||||
[ordering]: ../std/cmp/enum.Ordering.html
|
||||
|
||||
If it’s `Less`, we print `Too small!`, if it’s `Greater`, `Too big!`, and if
|
||||
`Equal`, `You win!`. `match` is really useful, and is used often in Rust.
|
||||
|
||||
I did mention that this won’t quite work yet, though. Let’s try it:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:28:21: 28:35 error: mismatched types:
|
||||
expected `&collections::string::String`,
|
||||
found `&_`
|
||||
(expected struct `collections::string::String`,
|
||||
found integral variable) [E0308]
|
||||
src/main.rs:28 match guess.cmp(&secret_number) {
|
||||
^~~~~~~~~~~~~~
|
||||
error: aborting due to previous error
|
||||
Could not compile `guessing_game`.
|
||||
```
|
||||
|
||||
Whew! This is a big error. The core of it is that we have ‘mismatched types’.
|
||||
Rust has a strong, static type system. However, it also has type inference.
|
||||
When we wrote `let guess = String::new()`, Rust was able to infer that `guess`
|
||||
should be a `String`, and so it doesn’t make us write out the type. And with
|
||||
our `secret_number`, there are a number of types which can have a value
|
||||
between one and a hundred: `i32`, a thirty-two-bit number, or `u32`, an
|
||||
unsigned thirty-two-bit number, or `i64`, a sixty-four-bit number. Or others.
|
||||
So far, that hasn’t mattered, and so Rust defaults to an `i32`. However, here,
|
||||
Rust doesn’t know how to compare the `guess` and the `secret_number`. They
|
||||
need to be the same type. Ultimately, we want to convert the `String` we
|
||||
read as input into a real number type, for comparison. We can do that
|
||||
with three more lines. Here’s our new program:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use std::cmp::Ordering;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
let guess: u32 = guess.trim().parse()
|
||||
.ok()
|
||||
.expect("Please type a number!");
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The new three lines:
|
||||
|
||||
```rust,ignore
|
||||
let guess: u32 = guess.trim().parse()
|
||||
.ok()
|
||||
.expect("Please type a number!");
|
||||
```
|
||||
|
||||
Wait a minute, I thought we already had a `guess`? We do, but Rust allows us
|
||||
to ‘shadow’ the previous `guess` with a new one. This is often used in this
|
||||
exact situation, where `guess` starts as a `String`, but we want to convert it
|
||||
to an `u32`. Shadowing lets us re-use the `guess` name, rather than forcing us
|
||||
to come up with two unique names like `guess_str` and `guess`, or something
|
||||
else.
|
||||
|
||||
We bind `guess` to an expression that looks like something we wrote earlier:
|
||||
|
||||
```rust,ignore
|
||||
guess.trim().parse()
|
||||
```
|
||||
|
||||
Followed by an `ok().expect()` invocation. Here, `guess` refers to the old
|
||||
`guess`, the one that was a `String` with our input in it. The `trim()`
|
||||
method on `String`s will eliminate any white space at the beginning and end of
|
||||
our string. This is important, as we had to press the ‘return’ key to satisfy
|
||||
`read_line()`. This means that if we type `5` and hit return, `guess` looks
|
||||
like this: `5\n`. The `\n` represents ‘newline’, the enter key. `trim()` gets
|
||||
rid of this, leaving our string with just the `5`. The [`parse()` method on
|
||||
strings][parse] parses a string into some kind of number. Since it can parse a
|
||||
variety of numbers, we need to give Rust a hint as to the exact type of number
|
||||
we want. Hence, `let guess: u32`. The colon (`:`) after `guess` tells Rust
|
||||
we’re going to annotate its type. `u32` is an unsigned, thirty-two bit
|
||||
integer. Rust has [a number of built-in number types][number], but we’ve
|
||||
chosen `u32`. It’s a good default choice for a small positive numer.
|
||||
|
||||
[parse]: ../std/primitive.str.html#method.parse
|
||||
[number]: primitive-types.html#numeric-types
|
||||
|
||||
Just like `read_line()`, our call to `parse()` could cause an error. What if
|
||||
our string contained `A👍%`? There’d be no way to convert that to a number. As
|
||||
such, we’ll do the same thing we did with `read_line()`: use the `ok()` and
|
||||
`expect()` methods to crash if there’s an error.
|
||||
|
||||
Let’s try our program out!
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 58
|
||||
Please input your guess.
|
||||
76
|
||||
You guessed: 76
|
||||
Too big!
|
||||
```
|
||||
|
||||
Nice! You can see I even added spaces before my guess, and it still figured
|
||||
out that I guessed 76. Run the program a few times, and verify that guessing
|
||||
the number works, as well as guessing a number too small.
|
||||
|
||||
Now we’ve got most of the game working, but we can only make one guess. Let’s
|
||||
change that by adding loops!
|
||||
|
||||
# Looping
|
||||
|
||||
The `loop` keyword gives us an infinite loop. Let’s add that in:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use std::cmp::Ordering;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
let guess: u32 = guess.trim().parse()
|
||||
.ok()
|
||||
.expect("Please type a number!");
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And try it out. But wait, didn’t we just add an infinite loop? Yup. Remember
|
||||
our discussion about `parse()`? If we give a non-number answer, we’ll `return`
|
||||
and quit. Observe:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 59
|
||||
Please input your guess.
|
||||
45
|
||||
You guessed: 45
|
||||
Too small!
|
||||
Please input your guess.
|
||||
60
|
||||
You guessed: 60
|
||||
Too big!
|
||||
Please input your guess.
|
||||
59
|
||||
You guessed: 59
|
||||
You win!
|
||||
Please input your guess.
|
||||
quit
|
||||
thread '<main>' panicked at 'Please type a number!'
|
||||
```
|
||||
|
||||
Ha! `quit` actually quits. As does any other non-number input. Well, this is
|
||||
suboptimal to say the least. First, let’s actually quit when you win the game:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use std::cmp::Ordering;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
let guess: u32 = guess.trim().parse()
|
||||
.ok()
|
||||
.expect("Please type a number!");
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!"),
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By adding the `break` line after the `You win!`, we’ll exit the loop when we
|
||||
win. Exiting the loop also means exiting the program, since it’s the last
|
||||
thing in `main()`. We have just one more tweak to make: when someone inputs a
|
||||
non-number, we don’t want to quit, we just want to ignore it. We can do that
|
||||
like this:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use std::cmp::Ordering;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
let guess: u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These are the lines that changed:
|
||||
|
||||
```rust,ignore
|
||||
let guess: u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => continue,
|
||||
};
|
||||
```
|
||||
|
||||
This is how you generally move from ‘crash on error’ to ‘actually handle the
|
||||
error’, by switching from `ok().expect()` to a `match` statement. The `Result`
|
||||
returned by `parse()` is an enum just like `Ordering`, but in this case, each
|
||||
variant has some data associated with it: `Ok` is a success, and `Err` is a
|
||||
failure. Each contains more information: the successful parsed integer, or an
|
||||
error type. In this case, we `match` on `Ok(num)`, which sets the inner value
|
||||
of the `Ok` to the name `num`, and then we just return it on the right-hand
|
||||
side. In the `Err` case, we don’t care what kind of error it is, so we just
|
||||
use `_` intead of a name. This ignores the error, and `continue` causes us
|
||||
to go to the next iteration of the `loop`.
|
||||
|
||||
Now we should be good! Let’s try:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 61
|
||||
Please input your guess.
|
||||
10
|
||||
You guessed: 10
|
||||
Too small!
|
||||
Please input your guess.
|
||||
99
|
||||
You guessed: 99
|
||||
Too big!
|
||||
Please input your guess.
|
||||
foo
|
||||
Please input your guess.
|
||||
61
|
||||
You guessed: 61
|
||||
You win!
|
||||
```
|
||||
|
||||
Awesome! With one tiny last tweak, we have finished the guessing game. Can you
|
||||
think of what it is? That’s right, we don’t want to print out the secret
|
||||
number. It was good for testing, but it kind of ruins the game. Here’s our
|
||||
final source:
|
||||
|
||||
```rust,ignore
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
use std::cmp::Ordering;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
println!("Please input your guess.");
|
||||
|
||||
let mut guess = String::new();
|
||||
|
||||
io::stdin().read_line(&mut guess)
|
||||
.ok()
|
||||
.expect("failed to read line");
|
||||
|
||||
let guess: u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Complete!
|
||||
|
||||
At this point, you have successfully built the Guessing Game! Congratulations!
|
||||
|
||||
This first project showed you a lot: `let`, `match`, methods, associated
|
||||
functions, using external crates, and more. Our next project will show off
|
||||
even more.
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<T:ToCStr>(&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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<T>`, 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<i32>) {
|
||||
// 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
|
||||
|
|
@ -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<T>` (specifically `Box<i32>` 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<T>`][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<i32>) {
|
||||
*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<i32>) {
|
||||
*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<i32>) {
|
||||
// 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<i32>) -> Box<i32> {
|
||||
*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<i32>) -> Box<i32> {
|
||||
*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<i32>) -> Vec<i32> {
|
||||
// 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<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, 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<T>`, because it has a single owner. We can do it with `Rc<T>` instead:
|
||||
|
||||
```rust
|
||||
use std::rc::Rc;
|
||||
|
||||
struct Car {
|
||||
name: String,
|
||||
}
|
||||
|
||||
struct Wheel {
|
||||
size: i32,
|
||||
owner: Rc<Car>,
|
||||
}
|
||||
|
||||
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<T>`, getting an `Rc<Car>`, and then use the
|
||||
`clone()` method to make new references. We've also changed our `Wheel` to have
|
||||
an `Rc<Car>` rather than just a `Car`.
|
||||
|
||||
This is the simplest kind of multiple ownership possible. For example, there's
|
||||
also `Arc<T>`, which uses more expensive atomic instructions to be the
|
||||
thread-safe counterpart of `Rc<T>`.
|
||||
|
||||
## 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<T:ToCStr>(&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.
|
||||
|
|
|
|||
|
|
@ -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<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, 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<i32>, v2: &Vec<i32>) -> 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<i32>`s as our arguments, we take a reference:
|
||||
`&Vec<i32>`. 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<i32>) {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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<String> to be moved
|
||||
the following is invalid as it requires the entire `Option<String>` 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
|
||||
"##,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue