some tweaks based on @aturon's feedback

This commit is contained in:
Steve Klabnik 2016-01-13 18:34:48 -05:00
parent e2a4f19a75
commit dcbd0d50b5

View File

@ -4,9 +4,9 @@ Rusts central feature is called ownership. It is a feature that is
straightforward to explain, but has deep implications for the rest of the straightforward to explain, but has deep implications for the rest of the
language. language.
Rust has a focus on safety and speed. It accomplishes these goals through many Rust is committed to both safety and speed. One of the key tools for balancing
zero-cost abstractions, which means that in Rust, abstractions cost as little between them is “zero-cost abstractions”: the various abstractions in Rust do
as possible in order to make them work. The ownership system is a prime example not pose a global performance penalty. The ownership system is a prime example
of a zero-cost abstraction. All of the analysis well talk about in this guide of a zero-cost abstraction. All of the analysis well talk about in this guide
is done at compile time. You do not pay any run-time cost for any of these is done at compile time. You do not pay any run-time cost for any of these
features. features.
@ -14,12 +14,12 @@ features.
However, this system does have a certain cost: learning curve. Many new However, this system does have a certain cost: learning curve. Many new
Rustaceans experience something we like to call fighting with the borrow Rustaceans experience something we like to call fighting with the borrow
checker, where the Rust compiler refuses to compile a program that the author checker, where the Rust compiler refuses to compile a program that the author
thinks is valid. This often happens because the programmers mental model of thinks is valid. This can happen because the programmer isnt used to thinking
how ownership should work doesnt match the actual rules that Rust implements. carefully about ownership, or is thinking about it differently from the way
You probably will experience similar things at first. There is good news, that Rust does. You probably will experience similar things at first. There is
however: more experienced Rust developers report that once they work with the good news, however: more experienced Rust developers report that once they work
rules of the ownership system for a period of time, they fight the borrow with the rules of the ownership system for a period of time, they fight the
checker less and less. Keep at it! borrow checker less and less. Keep at it!
This chapter will give you a foundation for understanding the rest of the This chapter will give you a foundation for understanding the rest of the
language. To do so, were going to learn through examples, focused around a language. To do so, were going to learn through examples, focused around a
@ -38,8 +38,8 @@ Anyway, here it is:
let s = "hello"; let s = "hello";
``` ```
This variable binding refers to a string. Its valid from the point at which This variable binding refers to a string literal. Its valid from the point at
its declared, until the end of the current _scope_. That is: which its declared, until the end of the current _scope_. That is:
```rust ```rust
{ // s is not valid here, its not yet in scope { // s is not valid here, its not yet in scope
@ -120,12 +120,12 @@ programming languages.
The second case, however, is different. In languages with a garbage collector, The second case, however, is different. In languages with a garbage collector,
the GC handles that second case, and we, as the programmer, dont need to think the GC handles that second case, and we, as the programmer, dont need to think
about it. In languages without a garbage collector, they often force you to about it. Languages without a garbage collector often force you to call a
call a second function to give the memory back. Part of the difficulty of second function to give the memory back. Part of the difficulty of using such
languages that work like this is knowing exactly when to do so. If we forget, languages is knowing exactly when to do so. If we forget, we will leak memory.
we will leak memory. If we do it too early, we will have an invalid variable. If we do it too early, we will have an invalid variable. If we do it twice,
If we do it twice, thats a bug too. We need to pair exactly one allocate thats a bug too. We need to pair exactly one allocate with exactly one
with exactly one free. free.
Rust takes a different path. Remember our example? Heres a version with Rust takes a different path. Remember our example? Heres a version with
`String`: `String`:
@ -190,10 +190,22 @@ too much if it doesnt make sense, and just ignore the capacity.
> chapter of the book. For now, thinking about `String` as a tuple is close > chapter of the book. For now, thinking about `String` as a tuple is close
> enough. > enough.
When we assign `s1` to `s2`, the `String` itself is copied. In other words: When we assign `s1` to `s2`, the `String` itself is copied. But not all kinds
of copying are the same. Many people draw distinctions between shallow
copying and deep copying. We dont use these terms in Rust. We instead say
that something is moved or cloned. Assignment in Rust causes a move. In
other words, it looks like this:
<img alt="s1 and s2" src="img/foo2.png" class="center" /> <img alt="s1 and s2" src="img/foo2.png" class="center" />
_Not_ this:
<img alt="s1 and s2 to two places" src="img/foo4.png" class="center" />
When moving, Rust makes a copy of the data structure itself, the contents of
`s1` are copied, but if `s1` contains a reference, like it does in this case,
Rust will not copy the things that those references refer to.
Theres a problem here! Both `data` pointers are pointing to the same place. Theres a problem here! Both `data` pointers are pointing to the same place.
Why is this a problem? Well, when `s2` goes out of scope, it will free the Why is this a problem? Well, when `s2` goes out of scope, it will free the
memory that `data` points to. And then `s1` goes out of scope, and it will memory that `data` points to. And then `s1` goes out of scope, and it will
@ -256,8 +268,9 @@ inexpensive.
## Clone ## Clone
But what if we _do_ want to copy the `String`s data? Theres a common method But what if we _do_ want to deeply copy the `String`s data, and not just the
for that: `clone()`. Heres an example of `clone()` in action: `String` itself? Theres a common method for that: `clone()`. Heres an example
of `clone()` in action:
```rust ```rust
let s1 = String::from("hello"); let s1 = String::from("hello");
@ -266,7 +279,8 @@ let s2 = s1.clone();
println!("{}", s1); println!("{}", s1);
``` ```
This will work just fine: This will work just fine. Remember our diagram from before? In this case,
it _is_ doing this:
<img alt="s1 and s2 to two places" src="img/foo4.png" class="center" /> <img alt="s1 and s2 to two places" src="img/foo4.png" class="center" />
@ -290,8 +304,9 @@ But why? We dont have a call to `clone()`. Why didnt `x` get moved into `y
For types that do not have any kind of complex storage requirements, like For types that do not have any kind of complex storage requirements, like
integers, typing `clone()` is busy work. Theres no reason we would ever want integers, typing `clone()` is busy work. Theres no reason we would ever want
to prevent `x` from being valid here, as theres no situation in which its to prevent `x` from being valid here, as theres no situation in which its
incorrect. In other words, a call to `clone()` would do nothing special over incorrect. In other words, theres no difference between deep and shallow
copying the data directly. copying here, so calling `clone()` wouldnt do anything differently from the
usual shallow copying.
Rust has a special annotation that you can place on types, called `Copy`. If Rust has a special annotation that you can place on types, called `Copy`. If
a type is `Copy`, an older binding is still usable after assignment. Integers a type is `Copy`, an older binding is still usable after assignment. Integers
@ -303,11 +318,11 @@ Remember `drop()`? Rust will not let you mark any type which has `drop()`
implemented as `Copy`. If you need to do something special when the value goes implemented as `Copy`. If you need to do something special when the value goes
out of scope, being `Copy` will be an error. out of scope, being `Copy` will be an error.
So what types are `Copy`? You can check the documentation for the given type So what types are `Copy`? You can check the documentation for the given type to
to be sure, but as a rule of thumb, any simple value that only represents some be sure, but as a rule of thumb, any any group of simple scalar values can be
memory can be `Copy`. Anything complicated will be the default, not-`Copy`. Copy, but nothing that requires allocation or is some form of resource is copy.
And you cant get it wrong: the compiler will throw an error if you try to And you cant get it wrong: the compiler will throw an error if you try to use
use a type that moves incorrectly, as we saw above. a type that moves incorrectly, as we saw above.
## Ownership and functions ## Ownership and functions
@ -362,6 +377,9 @@ fn makes_copy(some_integer: i32) { // some_integer comes into scope.
} // Here, some_integer goes out of scope. } // Here, some_integer goes out of scope.
``` ```
Remember: If we tried to use `s` after the call to `takes_ownership()`, Rust
would throw a compile-time error! These static checks protect us from mistakes.
Returning values can also transfer ownership: Returning values can also transfer ownership:
```rust ```rust