Updated snapshot of ch3

This commit is contained in:
Carol (Nichols || Goulding) 2022-08-11 16:33:21 -04:00 committed by Carol (Nichols || Goulding)
parent d8e1f9022f
commit 0ace243e9a
1 changed files with 4 additions and 141 deletions

View File

@ -96,8 +96,8 @@ change, so you dont have to keep track of it yourself. Your code is thus
easier to reason through.
But mutability can be very useful, and can make code more convenient to write.
Variables are immutable only by default; as you did in Chapter 2, you can make
them mutable by adding `mut` in front of the variable name. Adding `mut` also
Although variables are immutable by default, you can make them mutable by adding
`mut` in front of the variable name as you did in Chapter 2. Adding `mut` also
conveys intent to future readers of the code by indicating that other parts of
the code will be changing this variables value.
@ -129,14 +129,6 @@ Were allowed to change the value bound to `x` from `5` to `6` when `mut`
is used. Ultimately, deciding whether to use mutability or not is up to you and
depends on what you think is clearest in that particular situation.
<!--- Just to voice some thoughts here: there's a kind of bad pattern I see sometimes
with the Rust dev mindset around performance. In my experience it happens maybe less
often than you'd think that cloning shows up in the profile as a performance hit. I
wonder if we should maybe tone down or remove the discussion of performance above
because it's far stronger for the developer to pick a clear representation for their
program and then improve performance after they've found that model. /JT --->
<!-- Ok, I've removed the discussion of performance here. Good call. /Carol -->
### Constants
Like immutable variables, *constants* are values that are bound to a name and
@ -195,21 +187,6 @@ variable name to itself until either it itself is shadowed or the scope ends.
We can shadow a variable by using the same variables name and repeating the
use of the `let` keyword as follows:
<!--- A potential reword of the above for clarity:
As you saw in the guessing game tutorial in Chapter 2, you can declare a new
variable with the same name as a previous variable. Rustaceans say that the
first variable is *shadowed* by the second, which means that the second
variable is what the compiler will see when you use the name of the variable.
In effect, the second variable overshadows the first, taking any uses of the
variable name to itself until either it itself is shadowed or the scope ends.
We can shadow a variable by using the same variables name and repeating the
use of the `let` keyword as follows:
/JT --->
<!-- Sounds good to me, I've made the change to JT's version. What do you
think, Liz? /Carol -->
Filename: src/main.rs
```
@ -226,17 +203,6 @@ fn main() {
println!("The value of x is: {x}");
}
```
<!--- We haven't really introduced block scoping yet. I know we're starting
with variables, but I wonder if we should introduce scopes before shadowing,
or explain that each block has its own set of variables.
/JT --->
<!-- Chapter 4 goes into scopes in more detail. I feel like block scoping is a
pretty common programming concept, and the behavior of scopes in Rust that
we're demonstrating here is the same behavior as scopes have in most other
common programming languages. I don't recall getting comments from readers
being confused about scopes at this point. I added a small phrase in the next
paragraph that the curly brackets are creating a new scope... do you think
that's enough, Liz? /Carol -->
This program first binds `x` to a value of `5`. Then it creates a new variable
`x` by repeating `let x =`, taking the original value and adding `1` so the
@ -246,10 +212,6 @@ variable, multiplying the previous value by `2` to give `x` a value of `12`.
When that scope is over, the inner shadowing ends and `x` returns to being `6`.
When we run this program, it will output the following:
<!--- I lean towards reiterating that each `let x` is creating a new variable.
/JT -->
<!-- I've added a few mentions to that effect in the previous paragraph, what do you think, Liz? /Carol -->
```
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
@ -265,37 +227,12 @@ using the `let` keyword. By using `let`, we can perform a few transformations
on a value but have the variable be immutable after those transformations have
been completed.
<!--- so, to be clear, we're not overwriting the variable, so when the
shadowing variable goes out of scope the earlier variables become visible to
the compiler? --->
<!-- Well, we *are* overwriting it *in the inner scope* -- there's no way to
access the original value from the outer scope within the inner scope after
the shadowing. But yes, shadowing only applies to the scope it happens in,
which is what this example illustrates. Is there something that could be
made clearer? /Carol -->
<!-- JT, what do you think, is this clear enough as is or is there some way to clarify in the text? /LC -->
<!--- I made a couple notes above trying to see if we could tease out a good
explanation. Shadowing is effectively creating new variables and then these
variables get a kind of "higher priority" when you look up the same variable
name. Shadowing priority is kind of a "most recent wins", and it stays until
that variable is shadowed by a following one or that variable goes out of scope.
/JT -->
The other difference between `mut` and shadowing is that because 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 our
program asks a user to show how many spaces they want between some text by
inputting space characters, and then we want to store that input as a number:
<!--- Question: the further I read, the more I wonder if we should put the shadowing
stuff later. Is it valuable here as a kind of "building the right mental model" or
are we using up too much of our complexity budget for building that mental model
relatively early in the journey? Once we're introducing shadowing into new types
we're getting relatively deep into Rust-specific coding patterns /JT -->
<!-- I think it's important to address this here because shadowing is extremely
common in idiomatic Rust code, but can be unfamiliar. I'm not sure where it
would be appropriate to address if not here. /Carol -->
```
let spaces = " ";
let spaces = spaces.len();
@ -350,10 +287,6 @@ If we dont add the `: u32` type annotation above, Rust will display the
following error, which means the compiler needs more information from us to
know which type we want to use:
<!--- To help visual parsing, you might want to say "If we don't add the `: u32` type
annotation above... /JT --->
<!-- Done /Carol -->
```
$ cargo build
Compiling no_type_annotations v0.1.0 (file:///projects/no_type_annotations)
@ -632,28 +565,11 @@ This program creates the tuple `x` and then accesses each element of the tuple
using their respective indices. As with most programming languages, the first
index in a tuple is 0.
<!--- Indexing into a tuple using a constant, just like accessing a field of a struct,
I think is maybe a more natural way to think of this than thinking of `x.0`, `x.1`, etc
as separate variables. In the struct case, we don't think of each field as a separate
variable, but instead that there's a path to get to the contained values that can be
used and checked at compile time. /JT --->
<!-- I think JT was actually confused with what this paragraph was trying to
say, it was explaining that this particular example created new variables and
bound them to the values of the tuple elements, not that the tuple elements
*were* separate variables, so I've reworded this paragraph. Please check that
this makes sense, Liz! /Carol -->
The tuple without any values has a special name, *unit*. This value and its
corresponding type are both written `()` and represent an empty value or an
empty return type. Expressions implicitly return the unit value if they dont
return any other value.
<!--- It's trick to see the difference between `()` and `()`. Maybe we can say: "The
tuple without any values has a special name, *unit*. This value, and its corresponding
type -- also written `()` -- represent an empty value or an empty return type." /JT --->
<!-- I've tried to clear this up, but didn't take JT's suggestion exactly,
there were too many subphrases in my opinion /Carol -->
#### The Array Type
Another way to have a collection of multiple values is with an *array*. Unlike
@ -793,13 +709,6 @@ kind of error by immediately exiting instead of allowing the memory access and
continuing. Chapter 9 discusses more of Rusts error handling and how you can
write readable, safe code that neither panics nor allows invalid memory access.
<!--- I get the idea, though I'm feeling a little uneasy with leaving the reader
thinking "panic > invalid access" as the end of the story. Maybe we can tag something
on to the end: "Chapter 9 discusses more of Rust's error handling, and how you can
write readable, safe code that doesn't panic and doesn't allow invalid memory access.
/JT --->
<!-- I've incorporated JT's suggestion with a bit of rewording above /Carol -->
## Functions
Functions are prevalent in Rust code. Youve already seen one of the most
@ -836,14 +745,6 @@ called from inside the `main` function. Note that we defined `another_function`
as well. Rust doesnt care where you define your functions, only that theyre
defined somewhere in a scope that can be seen by the caller.
<!--- nit: Rust does want the functions in a place the caller can see. If they're
not in scope, Rust won't let the program build. Maybe we can say:
"only that they're defined somewhere the caller can see them".
or alt: "only that they're defined somewhere in a scope that can be seen by the
caller"
/JT --->
<!-- Done! /Carol -->
Lets start a new binary project named *functions* to explore functions
further. Place the `another_function` example in *src/main.rs* and run it. You
should see the following output:
@ -885,9 +786,6 @@ fn another_function(x: i32) {
}
```
<!--- nit: might want to use `{x}` /JT --->
<!-- Done! /Carol -->
Try running this program; you should get the following output:
```
@ -909,9 +807,6 @@ definitions means the compiler almost never needs you to use them elsewhere in
the code to figure out what type you mean. The compiler is also able to give
more helpful error messages if it knows what types the function expects.
<!--- Also helps give much better error messages /JT --->
<!-- Added a note! /Carol -->
When defining multiple parameters, separate the parameter declarations with
commas, like this:
@ -964,12 +859,6 @@ Weve actually already used statements and expressions. Creating a variable an
assigning a value to it with the `let` keyword is a statement. In Listing 3-1,
`let y = 6;` is a statement.
<!--- To help clarify how they're related, we could say that "`let y = 6;`" is a
statement, and the `6` being assigned to `y` is an expression. edit: I see we
say this later, just thought it might be a little nicer to give an examples of
each just following their definition. /JT --->
<!-- I think I'm going to leave this as-is /Carol -->
Filename: src/main.rs
```
@ -1007,14 +896,13 @@ error: expected expression, found statement (`let`)
|
= note: variable declaration using `let` is a statement
error[E0658]: `let` expressions in this position are experimental
error[E0658]: `let` expressions in this position are unstable
--> src/main.rs:2:14
|
2 | let x = (let y = 6);
| ^^^^^^^^^
|
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
warning: unnecessary parentheses around assigned value
--> src/main.rs:2:13
@ -1030,10 +918,6 @@ help: remove these parentheses
|
```
<!--- The errors in more recent Rust look slightly different here, if we want
to update before publication. /JT --->
<!-- Updated here and I will also check when we're in Word /Carol -->
The `let y = 6` statement does not return a value, so there isnt anything for
`x` to bind to. This is different from what happens in other languages, such as
C and Ruby, where the assignment returns the value of the assignment. In those
@ -1128,10 +1012,6 @@ Second, the `five` function has no parameters and defines the type of the
return value, but the body of the function is a lonely `5` with no semicolon
because its an expression whose value we want to return.
<!--- If you want, you could point out that the `println!` line that main ends
on is a statement, hence why main doesn't have a return value. /JT --->
<!-- I don't think I want to :) /Carol -->
Lets look at another example:
Filename: src/main.rs
@ -1179,7 +1059,7 @@ error[E0308]: mismatched types
| |
| implicitly returns `()` as its body has no tail or `return` expression
8 | x + 1;
| - help: consider removing this semicolon
| - help: remove this semicolon
```
The main error message, “mismatched types,” reveals the core issue with this
@ -1420,19 +1300,8 @@ fn main() {
}
```
<!--- Style nit: `{number}`. /JT --->
<!-- Fixed! /Carol -->
Listing 3-2: Assigning the result of an `if` expression to a variable
<!--- I was wondering when listings got numbered and when they didn't. Many of
the above don't get a number a title, though maybe it'd help readability? /JT --->
<!-- Liz: Chapter 3 doesn't have many listing numbers because on the first
round of printing, we hadn't really figured out what we were doing with listing
numbers yet. I'm happy to add more listing numbers in Chapter 3, but it'll take
me some time to go through and add appropriate captions, check cross
references, etc. Let me know if you'd like me to spend that time. /Carol -->
The `number` variable will be bound to a value based on the outcome of the `if`
expression. Run this code to see what happens:
@ -1548,10 +1417,6 @@ We also used `continue` in the guessing game, which in a loop tells the program
to skip over any remaining code in this iteration of the loop and go to the
next iteration.
<!--- Before you show loop labels below, you might want to give a code example
of using `break` to break a loop. /JT --->
<!-- I've rearranged the sections to take this suggestion here /Carol -->
#### Returning Values from Loops
One of the uses of a `loop` is to retry an operation you know might fail, such
@ -1587,8 +1452,6 @@ print the value in `result`, which in this case is 20.
#### Loop Labels to Disambiguate Between Multiple Loops
<!-- Liz: New heading for this section, what do you think? /Carol -->
If you have loops within loops, `break` and `continue` apply to the innermost
loop at that point. You can optionally specify a *loop label* on a loop that we
can then use with `break` or `continue` to specify that those keywords apply to