Standardize on fancy quotes

This commit is contained in:
Carol (Nichols || Goulding) 2016-10-31 19:33:28 -04:00
parent e8e42f79cb
commit fad86c54a2
10 changed files with 153 additions and 152 deletions

View File

@ -1,6 +1,6 @@
# Common Programming Concepts in Rust
Let's first look at concepts that appear in almost every programming language
Lets first look at concepts that appear in almost every programming language
and see how they work in Rust. Many programming languages have much in common
at their core. None of the concepts presented in this chapter are unique to
Rust, but well discuss Rusts particular syntax and conventions concerning

View File

@ -3,12 +3,12 @@
We mentioned in Chapter 2 that by default, variables are *immutable*.
This is one of many nudges in Rust that encourages us to write our code in a
way that gets the most of the safety and easy concurrency that Rust has to
offer. We still have the option to make our variables mutable, though. Let's
offer. We still have the option to make our variables mutable, though. Lets
explore how and why Rust encourages us to favor immutability, and why we might
want to opt out of that.
Variables being immutable means once a value is bound, you can't
change that value. To illustrate this, let's generate a new project in your
Variables being immutable means once a value is bound, you cant
change that value. To illustrate this, lets generate a new project in your
projects directory called `variables` by using `cargo new --bin variables`.
Then, in your new `variables` directory, open `src/main.rs` and replace its code
@ -43,17 +43,17 @@ note: prior assignment occurs here
This is our first example of the compiler helping us find an error in our
program! Compiler errors can be frustrating. Keep in mind that they only mean
your program isn't safely doing what you want it to do yet; they do *not* mean
that you're not a good programmer! Experienced Rustaceans still get compiler
your program isnt safely doing what you want it to do yet; they do *not* mean
that youre not a good programmer! Experienced Rustaceans still get compiler
errors. The Rust compiler is trying to help your program be the very best.
<!-- PROD: START BOX -->
> #### Extended Error Explanations
>
> Now that you've seen a Rust error, let's take a moment to look at one
> Now that youve seen a Rust error, lets take a moment to look at one
> particularly useful aspect of errors. Rust encourages you to seek further
> information on the kind of error you've received with output like this:
> information on the kind of error youve received with output like this:
>
> ```bash
> error: re-assignment of immutable variable `x` [--explain E0384]
@ -78,8 +78,8 @@ errors. The Rust compiler is trying to help your program be the very best.
> ```
> ````
>
> These explanations can really help if youre stuck on an error, so don't
> hesitate to look up the error code. The compiler is your friend, and it's
> These explanations can really help if youre stuck on an error, so dont
> hesitate to look up the error code. The compiler is your friend, and its
> there to help.
<!-- PROD: END BOX -->
@ -88,17 +88,17 @@ The error tells us that the cause of the error is `re-assignment of immutable
variable`, because we tried to assign a second value to the immutable `x`
variable.
It's important that we get compile-time errors when we attempt to change a
Its important that we get compile-time errors when we attempt to change a
value that we previously said was immutable because this very situation can
lead to bugs. If one part of our code operates on an assumption that a value
will never change, and another part of our code changes that value, it's
possible that the first part of the code won't do what it was designed to do.
will never change, and another part of our code changes that value, its
possible that the first part of the code wont do what it was designed to do.
This cause of bugs can be difficult to track down after the fact, especially
when the second piece of code only changes the value *sometimes*.
In Rust, we can trust that a value we say won't change really won't change,
In Rust, we can trust that a value we say wont change really wont change,
because the compiler is enforcing that guarantee for us. When reading and
writing code, we don't have to keep track in our head how and where a value
writing code, we dont have to keep track in our head how and where a value
might change. This can make code easier to reason about.
Mutability can be really useful, though! Variables are immutable only by
@ -131,9 +131,9 @@ The value of x is: 6
```
Using `mut`, we are allowed to change the value that `x` binds to from `5` to
`6`. In some cases you'll want to make a variable mutable because it makes the
`6`. In some cases youll want to make a variable mutable because it makes the
code easier to understand than an implementation that only uses immutable
variables. In cases where you're using large data structures, mutating an
variables. In cases where youre using large data structures, mutating an
instance in place may be faster than copying and returning newly allocated
instances. It all depends on the tradeoffs you want to make in your situation.
@ -142,8 +142,8 @@ instances. It all depends on the tradeoffs you want to make in your situation.
As we saw in the guessing game tutorial, we can declare new variables with the
same name as a previous variable, and the new variable *shadows* the previous
variable. We say that the first variable is *shadowed* by the second, which means
that the second variable's value is what you will see when you use the variable.
We can shadow a variable by using the same variable's name and repeating the use
that the second variables value is what you will see when you use the variable.
We can shadow a variable by using the same variables name and repeating the use
of the `let` keyword as follows:
Filename: src/main.rs
@ -174,11 +174,11 @@ The value of x is: 12
```
This is different from marking a variable as `mut` because unless we use the
`let` keyword again, we'll get a compile-time error if we accidentally try to
`let` keyword again, well get a compile-time error if we accidentally try to
reassign to this variable. We can perform a few transformations on a value, but
have the variable be immutable after those transformations have been completed.
The other difference between `mut` and shadowing is that, since we're
The other difference between `mut` and shadowing is that, since were
effectively creating a new variable when we use the `let` keyword again, we can
change the type of the value, but reuse the same name. For
example, say we ask a user to show us how many spaces they want between some
@ -202,7 +202,7 @@ spaces = spaces.len();
```
We will get a compile-time error because we are not allowed to mutate a
variable's type:
variables type:
```bash
error: mismatched types [--explain E0308]
@ -216,5 +216,5 @@ note: found type `usize`
error: aborting due to previous error
```
Now that we've explored how variables work, let's look at some more
Now that weve explored how variables work, lets look at some more
data types they can have.

View File

@ -1,7 +1,7 @@
## Data Types
Every value in Rust is of a certain *type*, which tells Rust what kind of data
is being given so it knows how to work with that data. In this section, we'll
is being given so it knows how to work with that data. In this section, well
look at a number of types built into the language itself. We split the types
into two subsets: scalar and compound.
@ -16,7 +16,7 @@ add a type annotation, like this:
let guess: u32 = "42".parse().unwrap();
```
If we don't put the type annotation here, Rust will give us this error that
If we dont put the type annotation here, Rust will give us this error that
means the compiler needs more information from us to know which possible type
we want:
@ -35,14 +35,14 @@ You will see some type annotations as we discuss the various data types.
A *scalar* type represents a single value. There are four primary scalar
types in Rust: integers, floating point numbers, booleans, and characters.
You'll likely recognize these from other programming languages, but let's jump
Youll likely recognize these from other programming languages, but lets jump
into how they work in Rust.
#### Integer Types
An *integer* is a number without a fractional component. We've used one integer
An *integer* is a number without a fractional component. Weve used one integer
type already in this chapter, the `i32` type. This type declaration indicates
that the value it's associated with should be a signed integer (hence the `i`,
that the value its associated with should be a signed integer (hence the `i`,
as opposed to a `u` for unsigned) for a 32-bit system. There are a number of
built-in integer types in Rust, shown in Table 3-1.
@ -62,11 +62,11 @@ Each variant can be either signed or unsigned and has an explicit size. Signed
and unsigned merely refers to whether it is possible for the number to be
either negative or positive; in other words, whether the number needs to have a
sign with it (signed), or whether it will only ever be positive and can
therefore be represented without a sign (unsigned). It's like writing numbers
therefore be represented without a sign (unsigned). Its like writing numbers
on paper: when the sign matters, a number is shown with a plus sign or minus
sign, but when it's safe to assume the number is positive, it's shown with no
sign, but when its safe to assume the number is positive, its shown with no
sign. Signed numbers are stored using twos complement representation (if
you're unsure what this is you can search for it online; an explanation is
youre unsure what this is you can search for it online; an explanation is
outside the scope of this text).
Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
@ -76,7 +76,7 @@ to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1, so a
`u8` can store from 0 to 2<sup>8</sup> - 1, which equals 0 to 255.
Finally, the `isize` and `usize` types depend on the kind of computer your
program is running on: 64-bits if you're on a 64-bit architecture, and 32-bits
program is running on: 64-bits if youre on a 64-bit architecture, and 32-bits
if youre on a 32-bit architecture.
You can write integer literals in any of the forms shown in Table 3-2. Note that
@ -93,15 +93,15 @@ all number literals except for the byte literal allow a type suffix, such as
*Table 3-2: Integer literals in Rust.*
So how do you know which type of integer to use? If you're unsure, Rust's
So how do you know which type of integer to use? If youre unsure, Rusts
defaults are generally good choices, and integer types default to `i32`: its
generally the fastest, even on 64-bit systems. The primary situation in which
you'd use `isize` or `usize` is when indexing some sort of collection.
youd use `isize` or `usize` is when indexing some sort of collection.
#### Floating-Point Types
Rust also has two primitive types for *floating-point numbers*, which are
numbers with decimal points. Rust's floating-point types are `f32` and `f64`,
numbers with decimal points. Rusts floating-point types are `f32` and `f64`,
which are 32 bits and 64 bits in size, respectively. The default type is `f64`,
as its roughly the same speed as `f32`, but has a larger precision. It is
possible to use an `f64` on 32 bit systems, but it will be slower than using an
@ -110,7 +110,7 @@ for better precision is a reasonable initial choice, and you should benchmark
your code if you suspect floating-point size is a problem in your case. See
Chapter XX for how to run benchmarks.
Here's an example showing floating-point numbers in action:
Heres an example showing floating-point numbers in action:
Filename: src/main.rs
@ -129,7 +129,7 @@ Floating-point numbers are represented according to the IEEE-754 standard. The
Rust supports the usual basic mathematic operations youd expect for all of
these number types: addition, subtraction, multiplication, division, and
remainder. This code shows how you'd use each one in a `let` statement:
remainder. This code shows how youd use each one in a `let` statement:
Filename: src/main.rs
@ -173,13 +173,13 @@ fn main() {
```
The main way to consume boolean values is through conditionals like an `if`
statement. Well cover how `if` statements work in Rust in the "Control Flow"
statement. Well cover how `if` statements work in Rust in the “Control Flow”
section of this chapter.
#### The Character Type
So far weve only worked with numbers, but Rust supports letters too. Rusts
`char` type is the language's most primitive alphabetic type, and this code
`char` type is the languages most primitive alphabetic type, and this code
shows one way to use it:
Filename: src/main.rs
@ -196,9 +196,9 @@ Rusts `char` represents a Unicode Scalar Value, which means that it can
represent a lot more than just ASCII. Accented letters, Chinese/Japanese/Korean
ideographs, emoji, and zero width spaces are all valid `char`s in Rust. Unicode
Scalar Values range from `U+0000` to `U+D7FF` and `U+E000` to `U+10FFFF`
inclusive. A "character" isnt really a concept in Unicode, however, so your
human intuition for what a "character" is may not match up with what a `char`
is in Rust. We'll discuss this in detail in the Strings section of Chapter 8.
inclusive. A “character” isnt really a concept in Unicode, however, so your
human intuition for what a “character” is may not match up with what a `char`
is in Rust. Well discuss this in detail in the Strings section of Chapter 8.
### Compound Types
@ -212,7 +212,7 @@ distinct types into one compound type.
We create a tuple by writing a comma-separated list of values inside
parentheses. Each position in the tuple has a distinct type, and the types of
the different values in the tuple do not have to be the same. We've added
the different values in the tuple do not have to be the same. Weve added
optional type annotations in this example:
Filename: src/main.rs
@ -288,14 +288,14 @@ fn main() {
```
While arrays can be useful since they are a primitive type so using them can be
very fast, they aren't as flexible as the vector type. The vector type is a
very fast, they arent as flexible as the vector type. The vector type is a
similar collection type provided by the standard library that *is* allowed to
grow or shrink in size. If you're unsure whether to use an array or a vector,
you should probably go with a vector, and we'll discuss them in more detail in
grow or shrink in size. If youre unsure whether to use an array or a vector,
you should probably go with a vector, and well discuss them in more detail in
Chapter 8.
An example of when we might want to use an array is storing the months of the
year. It's very unlikely that our program will need to add or remove months, so
year. Its very unlikely that our program will need to add or remove months, so
we can use an array since we know we will always have 12 items:
```rust
@ -353,14 +353,14 @@ error: Process didn't exit successfully: `target/debug/arrays` (exit code: 101)
```
We can see that the compilation did not give us any errors, but we got a
*runtime* error and our program didn't exit successfully. When we attempt to
access an element using indexing, Rust will check that the index we've
*runtime* error and our program didnt exit successfully. When we attempt to
access an element using indexing, Rust will check that the index weve
specified is less than the array length. If the index is greater than the
length, it will "panic", which is what it's called when a Rust program exits
length, it will *panic*, which is what its called when a Rust program exits
with an error.
This is our first example of Rusts safety principles in action. In many
low-level languages, this kind of check is not done, and when you provide an
incorrect index, invalid memory can be accessed. Rust protects us against this
kind of error by immediately exiting instead of allowing the memory access and
continuing. We'll discuss more of Rusts error handling in Chapter XX.
continuing. Well discuss more of Rusts error handling in Chapter XX.

View File

@ -2,12 +2,12 @@
Functions are pervasive in Rust code. Weve already seen one of the most
important functions in the language: the `main` function thats the entry
point of many programs. We've also seen the `fn` keyword, which allows us to
point of many programs. Weve also seen the `fn` keyword, which allows us to
declare new functions.
Rust code uses *snake case* as the conventional style for function and variable
names. In snake case, all letters are lower case, and there are underscores
separating words. Here's a program containing an example function definition:
separating words. Heres a program containing an example function definition:
Filename: src/main.rs
@ -108,7 +108,7 @@ type, but they just happen to be in this example. Our function then prints out
the values of both of its arguments.
Lets try out this code. Replace the program currently in your `function`
project's `main.rs` file with the example above, and run it as follows:
projects `main.rs` file with the example above, and run it as follows:
```bash
$ cargo run
@ -124,18 +124,18 @@ the two strings are printed with these values.
### Function Bodies
Function bodies are made up of a series of statements optionally ending in an
expression. So far, we've only seen functions without an ending expression, but
expression. So far, weve only seen functions without an ending expression, but
we have seen expressions as parts of statements. Since Rust is an
expression-based language, this is an important distinction to understand.
Other languages don't have the same distinctions, so let's look at what
Other languages dont have the same distinctions, so lets look at what
statements and expressions are and how their differences affect the bodies of
functions.
#### Statements and Expressions
We've already been using both statements and expressions. *Statements* are
Weve already been using both statements and expressions. *Statements* are
instructions that perform some action and do not return a value. *Expressions*
evaluate to a resulting value. Let's look at some examples.
evaluate to a resulting value. Lets look at some examples.
Creating a variable and assigning a value to it with the `let` keyword
is a statement. In this example, `let y = 6;` is a statement:
@ -177,7 +177,7 @@ error: aborting due to previous error
error: Could not compile `functions`.
```
The `let y = 6` statement does not return a value, so there isn't anything for
The `let y = 6` statement does not return a value, so there isnt anything for
`x` to bind to. This is different than in other languages like C and Ruby where
the assignment returns the value of the assignment. In those languages, we can
write `x = y = 6` and have both `x` and `y` have the value `6`; that is not the
@ -213,7 +213,7 @@ fn main() {
```
<!-- If we use wingding numbers to call out code, we might delete the
repetition here and just use those numbers--that can help the flow of the text.
I'm flagging this as a reminder for when we transfer to libreoffice -->
Im flagging this as a reminder for when we transfer to libreoffice -->
The expression:
@ -228,7 +228,7 @@ is a block that, in this case, evaluates to `4`, and then gets bound to
`y` as part of the `let` statement.
Note that the line containing `x + 1` does not have a semicolon at the end,
unlike most of the lines we've seen up until now. This is the most important
unlike most of the lines weve seen up until now. This is the most important
distinction between expressions and statements to remember: statements end in
semicolons while expressions do not. If you add a semicolon to the end of an
expression, that will turn it into a statement, which will then not return a
@ -238,8 +238,8 @@ value. Keep this in mind as we explore function return values and expressions.
Functions can return values back to the code that calls them. We dont name
return values, but we do declare their type, after an arrow (`->`). In Rust,
the "return value of the function” is synonymous with the "value of the final
expression in the block of the body of a function.” Here's an example of a
the “return value of the function” is synonymous with the “value of the final
expression in the block of the body of a function.” Heres an example of a
function that returns a value:
Filename: src/main.rs
@ -257,8 +257,8 @@ fn main() {
```
There are no function calls, macros, or even `let` statements in the `five`
function: just the number `5` by itself. That's a perfectly valid function in
Rust. Note the function's return type is specified, too, as `-> i32`. Try
function: just the number `5` by itself. Thats a perfectly valid function in
Rust. Note the functions return type is specified, too, as `-> i32`. Try
running this code, and the output should look like this:
```bash
@ -268,7 +268,7 @@ $ cargo run
The value of x is: 5
```
The `5` in `five` is the function's return value, which is why the return type
The `5` in `five` is the functions return value, which is why the return type
is `i32`. Lets examine this in more detail. There are two important bits.
First, the line `let x = five();` shows us using the return value of a function
to initialize a variable.
@ -282,7 +282,7 @@ let x = 5;
The second interesting bit is the `five` function itself. It requires no
arguments and defines the type of the return value, but the body of the
function is a lonely `5` with no semicolon because it is an expression whose
value we want to return. Let's look at another example:
value we want to return. Lets look at another example:
Filename: src/main.rs
@ -334,7 +334,7 @@ error: aborting due to previous error
error: Could not compile `functions`.
```
The main error message, "not all control paths return a value", reveals the
The main error message, “not all control paths return a value”, reveals the
core of the issue with this code. The definition of the function `plus_one`
says that it will return an `i32`, but statements dont evaluate to a value.
Therefore, nothing is returned, which contradicts the function definition and

View File

@ -12,7 +12,7 @@ Heres a simple comment:
```
In Rust, comments must start with two slashes and will last until the end of
the line. For comments that extend beyond a single line, you'll need to
the line. For comments that extend beyond a single line, youll need to
include `//` on each line, like this:
```rust

View File

@ -9,8 +9,8 @@ and loops.
### `if` Expressions
An `if` expression allows us to branch our code depending on conditions. We
provide a condition and then say, "If this condition is met, run this
block of code. If the condition is not met, do not run this block of code."
provide a condition and then say, If this condition is met, run this
block of code. If the condition is not met, do not run this block of code.
Lets make a new project to explore `if`, called `branches`. In `src/main.rs`,
put:
@ -36,7 +36,7 @@ if the condition is true goes immediately after the condition, inside curly
braces. These blocks are sometimes called *arms*. We can optionally also
include an `else` expression, which we have chosen to do here. This gives the
program an alternative block of code to execute should the condition evaluate
to false. If you don't give an `else` expression and the condition is false,
to false. If you dont give an `else` expression and the condition is false,
the program will just skip the `if` block and move on to the next bit of code.
Try running this code, and you should see output like this:
@ -65,7 +65,7 @@ condition was false
```
Its also worth noting that the condition here *must* be a `bool`. To see what
happens if the condition isn't a `bool`, try running this code:
happens if the condition isnt a `bool`, try running this code:
Filename: src/main.rs
@ -153,12 +153,12 @@ When this program executes, it will check each `if` expression in turn and
execute the first body for which the condition holds true. Note that even
though 6 is divisible by 2, we did not see the output `number is divisible by
2`, nor did we see the `number is not divisible by 4, 3, or 2` text from the
`else` block. That's because Rust will only execute the block for the first
true condition, and once it finds one, it won't even check the rest.
`else` block. Thats because Rust will only execute the block for the first
true condition, and once it finds one, it wont even check the rest.
Using too many `else if` expressions can clutter your code, so if you find
yourself with more than one, you may want to look at refactoring your code. In
Chapter 6, we'll talk about a powerful Rust branching construct called `match`
Chapter 6, well talk about a powerful Rust branching construct called `match`
for these cases.
#### Using `if` in a `let` statement
@ -239,7 +239,7 @@ The expression in the `if` block evaluates to an integer and the expression in
the `else` block evaluates to a string. This cant work, because variables
must have a single type. Rust needs to know at compile time what type
the `number` variable is, definitively, so that it can verify at compile time
that its type is valid everywhere we use `number`. Rust wouldn't be able to do
that its type is valid everywhere we use `number`. Rust wouldnt be able to do
that if the type of `number` was only determined at runtime; the compiler would
be more complex and be able to make fewer guarantees about our code if it had
to keep track of multiple hypothetical types for any variable.
@ -287,7 +287,7 @@ again!
^Cagain!
```
That `^C` there is where we hit `control-c`. You may or may not see "again!"
That `^C` there is where we hit `control-c`. You may or may not see `again!`
printed after the `^C`, depending on where the code was in the loop when it
received the signal to halt.
@ -299,14 +299,14 @@ correctly.
#### Conditional Loops With `while`
It's often useful for a program to have a condition that can be evaluated
Its often useful for a program to have a condition that can be evaluated
within a loop. While the condition is true, the loop runs. When the condition
ceases to be true, we call `break`, stopping the loop. This could be
implemented with a combination of `loop`, `if`, `else`, and `break`; you could
try that now in a program, if you'd like.
try that now in a program, if youd like.
But this pattern is so common that Rust has a more efficient language construct
for it, called a `while` loop. Here's an example using `while`: this program
for it, called a `while` loop. Heres an example using `while`: this program
loops three times, counting down each time. Finally, after the loop, it prints
another message, then exits:
@ -327,7 +327,7 @@ fn main() {
```
This gets rid of a lot of nesting that would be necessary if we used `loop`,
`if`, `else`, and `break`, and it's more clear. While a condition holds, run
`if`, `else`, and `break`, and its more clear. While a condition holds, run
this code; otherwise, exit the loop.
#### Looping Through a Collection with `for`
@ -350,7 +350,7 @@ fn main() {
}
```
Here, we're counting up through the elements in the array. We start at index 0,
Here, were counting up through the elements in the array. We start at index 0,
then loop until we hit the final index of our array (that is, when `index < 5`
is no longer true). Running this will print out every element of the array:
@ -370,7 +370,7 @@ will reach a value of `6` at some point, the loop stops executing before trying
to fetch a sixth value from the array.
This approach is error-prone, though; we could cause our program to panic by
getting the index length incorrect. It's also slow, as the compiler needs to
getting the index length incorrect. Its also slow, as the compiler needs to
perform the conditional check on every element on every iteration through the
loop.
@ -389,8 +389,8 @@ fn main() {
}
```
If we run this, we'll see the same output as the previous example. Importantly,
though, we've now increased the safety of our code and eliminated the chance of
If we run this, well see the same output as the previous example. Importantly,
though, weve now increased the safety of our code and eliminated the chance of
bugs that might result from going beyond the end of the array or not going far
enough and missing some items.
@ -400,7 +400,7 @@ item from the `a` array but forgot to update the condition to `while index <
remember to change any other code if we changed the number of values in the
array.
If you're wondering about the `iter` code in this example, keep reading! We
If youre wondering about the `iter` code in this example, keep reading! We
will cover method syntax generally in Chapter XX and iterators specifically in
Chapter XX.
@ -411,8 +411,8 @@ Rustaceans would use a `for` loop. The way to do that would be to use a
`Range`, which is a type provided by the standard library that generates all
numbers in sequence starting from one number and ending before another number.
Here's what the countdown would look like with a for loop, and using another
method we haven't yet talked about, `rev`, to reverse the range:
Heres what the countdown would look like with a for loop, and using another
method we havent yet talked about, `rev`, to reverse the range:
Filename: src/main.rs
@ -425,12 +425,12 @@ fn main() {
}
```
That's a bit nicer, isn't it?
Thats a bit nicer, isnt it?
## Summary
You made it! That was a big chapter: we covered variables, scalar and
compound data types, functions, comments, `if` expressions, and loops! If you'd
compound data types, functions, comments, `if` expressions, and loops! If youd
like to get some practice with the concepts in this chapter, try building
programs to do things like:
@ -439,5 +439,5 @@ programs to do things like:
* Print the lyrics to the Christmas carol *The Twelve Days of Christmas*,
taking advantage of the repetition in the song.
When you're ready to move on, we'll talk about a concept in Rust that *doesn't*
When youre ready to move on, well talk about a concept in Rust that *doesnt*
commonly exist in other programming languages: ownership.

View File

@ -1,7 +1,7 @@
# Understanding Ownership
Ownership is Rust's most unique feature and enables Rust to make memory safety
guarantees without needing a garbage collector. It's therefore important to
understand how ownership works in Rust. In this chapter we'll talk about
Ownership is Rusts most unique feature, and enables Rust to make memory safety
guarantees without needing a garbage collector. Its therefore important to
understand how ownership works in Rust. In this chapter well talk about
ownership as well as several related features: borrowing, slices, and how Rust
lays things out in memory.

View File

@ -3,7 +3,7 @@
Rusts central feature is *ownership*. It is a feature that is straightforward
to explain, but has deep implications for the rest of the language.
All programs have to manage the way they use a computer's memory while running.
All programs have to manage the way they use a computers memory while running.
Some languages have garbage collection, while in others, the programmer has to
explicitly allocate and free the memory. Rust takes a third approach: memory is
managed through a system of ownership with a set of rules that the compiler
@ -12,21 +12,21 @@ features.
Since ownership is a new concept for many programmers, it does take
some time to get used to. There is good news, though: the more experienced you
become with Rust and the rules of the ownership system, the more you'll be
become with Rust and the rules of the ownership system, the more youll be
able to naturally develop code that is both safe and efficient. Keep at it!
Once you understand ownership, you have a good foundation for understanding the
features that make Rust unique. In this chapter, we'll learn ownership by going
features that make Rust unique. In this chapter, well learn ownership by going
through some examples, focusing on a very common data structure: strings.
<!-- PROD: START BOX -->
> #### The Stack and the Heap
>
> In many programming languages, we don't have to think about the stack and the
> In many programming languages, we dont have to think about the stack and the
> heap very often. But in a systems programming language like Rust, whether a
> value is on the stack or the heap has more of an effect on how the language
> behaves and why we have to make certain decisions. We're going to be
> behaves and why we have to make certain decisions. Were going to be
> describing parts of ownership in relation to the stack and the heap, so here
> is a brief explanation.
>
@ -36,7 +36,7 @@ through some examples, focusing on a very common data structure: strings.
> This is referred to as *last in, first out*. Think of a stack of plates: when
> you add more plates, you put them on top of the pile, and when you need a
> plate, you take one off the top. Adding or removing plates from the middle or
> bottom wouldn't work as well! Adding data is called *pushing onto the stack*
> bottom wouldnt work as well! Adding data is called *pushing onto the stack*
> and removing data is called *popping off the stack*.
>
> The stack is fast because of the way it accesses the data: it never has to
@ -49,7 +49,7 @@ through some examples, focusing on a very common data structure: strings.
> when we put data on the heap, we ask for some amount of space. The operating
> system finds an empty spot somewhere in the heap that is big enough, marks it
> as being in use, and returns to us a pointer to that location. This process
> is called *allocating on the heap*, and sometimes we just say "allocating"
> is called *allocating on the heap*, and sometimes we just say *allocating*
> for short. Pushing values onto the stack is not considered allocating. Since
> the pointer is a known, fixed size, we can store the pointer on the stack,
> but when we want the actual data, we have to follow the pointer.
@ -63,14 +63,14 @@ through some examples, focusing on a very common data structure: strings.
> get there. Allocating a large amount of space can also take time.
>
> When our code calls a function, the values passed into the function
> (including, potentially, pointers to data on the heap) and the function's
> (including, potentially, pointers to data on the heap) and the functions
> local variables get pushed onto the stack. When the function is over, those
> values get popped off the stack.
>
> Keeping track of what parts of code are using what data on the heap,
> minimizing the amount of duplicate data on the heap, and cleaning up unused
> data on the heap so that we don't run out of space are all problems that
> ownership addresses. Once you understand ownership, you won't need to think
> data on the heap so that we dont run out of space are all problems that
> ownership addresses. Once you understand ownership, you wont need to think
> about the stack and the heap very often, but knowing that managing heap data
> is why ownership exists can help explain why it works the way it does.
@ -78,7 +78,7 @@ through some examples, focusing on a very common data structure: strings.
### Ownership Rules
First, let's take a look at the rules. Keep these in mind as we go through the
First, lets take a look at the rules. Keep these in mind as we go through the
examples that will illustrate the rules:
> 1. Each value in Rust has a variable thats called its *owner*.
@ -87,16 +87,16 @@ examples that will illustrate the rules:
### Variable Scope
We've walked through an example of a Rust program already in the tutorial
Weve walked through an example of a Rust program already in the tutorial
chapter. Now that were past basic syntax, we wont include all of the `fn
main() {` stuff in examples, so if youre following along, you will have to put
the following examples inside of a `main` function yourself. This lets our
examples be a bit more concise, letting us focus on the actual details rather
than boilerplate.
As a first example of ownership, we'll look at the *scope* of some variables.
As a first example of ownership, well look at the *scope* of some variables.
A scope is the range within a program for which an item is valid.
Let's say we have a variable that looks like this:
Lets say we have a variable that looks like this:
```rust
let s = "hello";
@ -125,17 +125,17 @@ build on top of this understanding by introducing the `String` type.
### The `String` Type
In order to illustrate the rules of ownership, we need a data type that is more
complex than the ones we covered in Chapter 3. All of the data types we've
complex than the ones we covered in Chapter 3. All of the data types weve
looked at previously are stored on the stack and popped off the stack when
their scope is over, but we want to look at data that is stored on the heap and
explore how Rust knows when to clean that data up.
We're going to use `String` as the example here and concentrate on the parts of
Were going to use `String` as the example here and concentrate on the parts of
`String` that relate to ownership. These aspects also apply to other complex
data types provided by the standard library and that you create. We'll go into
data types provided by the standard library and that you create. Well go into
more depth about `String` specifically in Chapter XX.
We've already seen string literals, where a string value is hard-coded into our
Weve already seen string literals, where a string value is hard-coded into our
program. String literals are convenient, but they arent always suitable for
every situation you want to use text. For one thing, theyre immutable. For
another, not every string value can be known when we write our code: what if we
@ -189,9 +189,9 @@ implementation requests the memory it needs. This is pretty much universal in
programming languages.
The second case, however, is different. In languages with a *garbage collector*
(GC), the GC will keep track and clean up memory that isn't being used anymore,
(GC), the GC will keep track and clean up memory that isnt being used anymore,
and we, as the programmer, dont need to think about it. Without GC, its the
programmer's responsibility to identify when memory is no longer being used and
programmers responsibility to identify when memory is no longer being used and
call code to explicitly return it, just as we did to request it. Doing this
correctly has historically been a difficult problem in programming. If we
forget, we will waste memory. If we do it too early, we will have an invalid
@ -230,7 +230,7 @@ allocated on the heap. Lets go over some of those situations now.
#### Ways Variables and Data Interact: Move
There are different ways that multiple variables can interact with the same data
in Rust. Let's take an example using an integer:
in Rust. Lets take an example using an integer:
```rust
let x = 5;
@ -252,7 +252,7 @@ let s2 = s1;
This looks very similar to the previous code, so we might assume that the way
it works would be the same: that the second line would make a copy of the value
in `s1` and bind it to `s2`. This isn't quite what happens.
in `s1` and bind it to `s2`. This isnt quite what happens.
To explain this more thoroughly, lets take a look at what `String` looks like
under the covers in Figure 4-1. A `String` is made up of three parts, shown on
@ -262,24 +262,24 @@ is the memory that holds the contents, and this is on the heap.
<img alt="String in memory" src="img/trpl04-01.svg" class="center" style="width: 50%;" />
Figure 4-1: Representation in memory of a `String` holding the value "hello"
Figure 4-1: Representation in memory of a `String` holding the value `"hello"`
bound to `s1`
The length is how much memory, in bytes, the contents of the `String` is
currently using. The capacity is the total amount of memory, in bytes, that the
`String` has gotten from the operating system. The difference between length
and capacity matters but not in this context, so for now, it's fine to ignore
and capacity matters but not in this context, so for now, its fine to ignore
the capacity.
When we assign `s1` to `s2`, the `String` data itself is copied, meaning we
copy the pointer, the length, and the capacity that are on the stack. We do not
copy the data on the heap that the `String`'s pointer refers to. In other
copy the data on the heap that the `String`s pointer refers to. In other
words, it looks like figure 4-2.
<img alt="s1 and s2 pointing to the same value" src="img/trpl04-02.svg" class="center" style="width: 50%;" />
Figure 4-2: Representation in memory of the variable `s2` that has a copy of
`s1`'s pointer, length and capacity
`s1`s pointer, length and capacity
And *not* Figure 4-3, which is what memory would look like if Rust instead
copied the heap data as well. If Rust did this, the operation `s2 = s1` could
@ -298,7 +298,7 @@ same memory. This is known as a *double free* error and is one of the memory
safety bugs we mentioned before. Freeing memory twice can lead to memory
corruption, which can potentially lead to security vulnerabilities.
In order to ensure memory safety, there's one more detail to what happens in
In order to ensure memory safety, theres one more detail to what happens in
this situation in Rust. Instead of trying to copy the allocated memory, Rust
says that `s1` is no longer valid and, therefore, doesnt need to free anything
when it goes out of scope. Check out what happens when you try to use `s1`
@ -323,11 +323,11 @@ println!("{}", s1);
^~
```
If you have heard the terms "shallow copy" and "deep copy" while working with
If you have heard the terms “shallow copy” and “deep copy” while working with
other languages, the concept of copying the pointer, length, and capacity
without copying the data probably sounds like a shallow copy. But because Rust
also invalidates the first variable, instead of calling this a shallow copy,
it's known as a *move*. Here we would read this by saying that `s1` was *moved*
its known as a *move*. Here we would read this by saying that `s1` was *moved*
into `s2`. So what actually happens looks like Figure 4-4.
<img alt="s1 moved to s2" src="img/trpl04-04.svg" class="center" style="width: 50%;" />
@ -338,7 +338,7 @@ That solves our problem! With only `s2` valid, when it goes out of scope, it
alone will free the memory, and were done.
Furthermore, theres a design choice thats implied by this: Rust will never
automatically create "deep" copies of your data. Therefore, any *automatic*
automatically create “deep” copies of your data. Therefore, any *automatic*
copying can be assumed to be inexpensive.
#### Ways Variables and Data Interact: Clone
@ -379,18 +379,18 @@ let y = x;
println!("x = {}, y = {}", x, y);
```
This seems to contradict what we just learned: we don't have a call to
`clone`, but `x` is still valid, and wasn't moved into `y`.
This seems to contradict what we just learned: we dont have a call to `clone`,
but `x` is still valid, and wasnt moved into `y`.
This is because types like integers that have a known size at compile time are
stored entirely on the stack, so copies of the actual values are quick to make.
That means there's no reason we would want to prevent `x` from being valid
That means theres no reason we would want to prevent `x` from being valid
after we create the variable `y`. In other words, theres no difference between
deep and shallow copying here, so calling `clone` wouldnt do anything
differently from the usual shallow copying and we can leave it out.
Rust has a special annotation called the `Copy` trait that we can place on
types like these (we'll talk more about traits in Chapter XX). If a type has
types like these (well talk more about traits in Chapter XX). If a type has
the `Copy` trait, an older variable is still usable after assignment. Rust will
not let us annotate a type with the `Copy` trait if the type, or any of its
parts, has implemented `drop`. If the type needs something special to happen
@ -421,7 +421,7 @@ Filename: src/main.rs
fn main() {
let s = String::from("hello"); // s comes into scope.
takes_ownership(s); // s's value moves into the function...
takes_ownership(s); // ss value moves into the function...
// ... and so is no longer valid here.
let x = 5; // x comes into scope.
@ -429,7 +429,7 @@ fn main() {
// but i32 is Copy, so its okay to still
// use x afterward.
} // Here, x goes out of scope, then s. But since s's value was moved, nothing
} // Here, x goes out of scope, then s. But since ss value was moved, nothing
// special happens.
fn takes_ownership(some_string: String) { // some_string comes into scope.
@ -449,7 +449,8 @@ and where the ownership rules prevent you from doing so.
### Return Values and Scope
Returning values can also transfer ownership. Here's an example with similar annotations:
Returning values can also transfer ownership. Heres an example with similar
annotations:
Filename: src/main.rs
@ -485,7 +486,7 @@ fn takes_and_gives_back(a_string: String) -> String { // a_string comes into sco
```
Its the same pattern, every time: assigning a value to another variable moves
it, and when heap data values' variables go out of scope, if the data hasnt
it, and when heap data values variables go out of scope, if the data hasnt
been moved to be owned by another variable, the value will be cleaned up by
`drop`.

View File

@ -63,7 +63,7 @@ fn calculate_length(s: &String) -> usize { // s is a reference to a String
```
Its the same process as before, but we dont drop what the reference points to
when it goes out of scope because we don't have ownership. This lets us write
when it goes out of scope because we dont have ownership. This lets us write
functions which take references as arguments instead of the values themselves,
so that we wont need to return them to give back ownership.
@ -71,7 +71,7 @@ We call this process *borrowing*. Just like with real life, if a person owns
something, you can borrow it from them, and when youre done, you have to give
it back.
So what happens if we try to modify something we're borrowing? Try this code
So what happens if we try to modify something were borrowing? Try this code
out. Spoiler alert: it doesnt work!
Filename: src/main.rs
@ -156,10 +156,10 @@ mutate whenever youd like. The benefit of having this restriction is that Rus
can prevent data races at compile time. A *data race* is a particular type of
race condition where two or more pointers access the same data at the same
time, at least one of the pointers is being used to write to the data, and
there's no mechanism being used to synchronize access to the data. Data races
theres no mechanism being used to synchronize access to the data. Data races
cause undefined behavior and can be difficult to diagnose and fix when trying
to track them down at runtime; Rust prevents this problem from happening since
it won't even compile code with data races!
it wont even compile code with data races!
As always, we can use `{}`s to create a new scope, allowing for multiple mutable
references, just not *simultaneous* ones:
@ -204,17 +204,17 @@ error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immuta
Whew! We *also* cannot have a mutable reference while we have an immutable one.
Users of an immutable reference dont expect the values to suddenly change out
from under them! Multiple immutable references are okay, however, since no one
who is just reading the data has the ability to affect anyone else's reading of
who is just reading the data has the ability to affect anyone elses reading of
the data.
Even though these errors may be frustrating at times, remember that it's the
Even though these errors may be frustrating at times, remember that its the
Rust compiler pointing out a potential bug earlier (at compile time rather than
at runtime) and showing you exactly where the problem is instead of you having
to track down why sometimes your data isn't what you thought it should be.
to track down why sometimes your data isnt what you thought it should be.
### Dangling References
In languages with pointers, it's easy to make the error of creating a *dangling
In languages with pointers, its easy to make the error of creating a *dangling
pointer*, a pointer referencing a location in memory that may have been given
to someone else, by freeing some memory while keeping around a pointer to that
memory. In Rust, by contrast, the compiler guarantees that references will
@ -255,12 +255,12 @@ error: aborting due to previous error
```
This error message refers to a feature we havent learned about yet:
*lifetimes*. We'll discuss lifetimes in detail in Chapter XX, but, disregarding
*lifetimes*. Well discuss lifetimes in detail in Chapter XX, but, disregarding
the parts about lifetimes, the message does contain the key to why this code is
a problem: `this functions return type contains a borrowed value, but there is
no value for it to be borrowed from`.
Lets have a closer look at exactly what's happening at each stage of our
Lets have a closer look at exactly whats happening at each stage of our
`dangle` code:
```rust,ignore
@ -299,4 +299,4 @@ Heres a recap of what weve talked about:
2. Any number of immutable references.
2. References must always be valid.
Next, let's look at a different kind of reference: slices.
Next, lets look at a different kind of reference: slices.

View File

@ -102,13 +102,13 @@ fn main() {
s.clear(); // This empties the String, making it equal to "".
// word still has the value 5 here, but there's no more string that
// word still has the value 5 here, but theres no more string that
// we could meaningfully use the value 5 with. word is now totally invalid!
}
```
This program compiles without any errors, and also would if we used `word`
after calling `s.clear()`. `word` isn't connected to the state of `s` at all,
after calling `s.clear()`. `word` isnt connected to the state of `s` at all,
so `word` still contains the value `5`. We could use that `5` with `s` to try
to extract the first word out, but this would be a bug since the contents of
`s` have changed since we saved `5` in `word`.
@ -121,7 +121,7 @@ fn second_word(s: &String) -> (usize, usize) {
```
Now were tracking both a start *and* an ending index, and we have even more
values that were calculated from data in a particular state but aren't tied to
values that were calculated from data in a particular state but arent tied to
that state at all. We now have three unrelated variables floating
around which need to be kept in sync.
@ -190,7 +190,7 @@ let slice = &s[..];
```
With this in mind, lets re-write `first_word` to return a slice. The type
that signifies "string slice" is written as `&str`:
that signifies “string slice” is written as `&str`:
Filename: src/main.rs
@ -225,7 +225,7 @@ fn second_word(s: &String) -> &str {
We now have a straightforward API thats much harder to mess up. Remember our
bug from before, when we got the first word but then cleared the string so that
our first word was invalid? That code was logically incorrect but didn't show
our first word was invalid? That code was logically incorrect but didnt show
any immediate errors. The problems would show up later, if we kept trying to
use the first word index with an emptied string. Slices make this bug
impossible, and let us know we have a problem with our code much sooner. Using
@ -361,9 +361,9 @@ The concepts of ownership, borrowing, and slices are what ensure memory safety
in Rust programs at compile time. Rust is a language that gives you control
over your memory usage like other systems programming languages, but having the
owner of data automatically clean up that data when the owner goes out of scope
means you don't have to write and debug extra code to get this control.
means you dont have to write and debug extra code to get this control.
Ownership affects how lots of other parts of Rust work, so we will be talking
about these concepts further throughout the rest of the book. Let's move on to
the next chapter where we'll look at grouping pieces of data together in a
about these concepts further throughout the rest of the book. Lets move on to
the next chapter where well look at grouping pieces of data together in a
`struct`.