diff --git a/src/borrowing/borrowck.md b/src/borrowing/borrowck.md
index a8508927..306fa6a3 100644
--- a/src/borrowing/borrowck.md
+++ b/src/borrowing/borrowck.md
@@ -31,7 +31,7 @@ fn main() {
* The above code does not compile because `a` is borrowed as mutable (through `c`) and as immutable (through `b`) at the same time.
* Move the `println!` statement for `b` before the scope that introduces `c` to make the code compile.
* After that change, the compiler realizes that `b` is only ever used before the new mutable borrow of `a` through `c`. This is a feature of the borrow checker called "non-lexical lifetimes".
-* The exclusive reference constraint is quite strong. Rust uses it to ensure that data races do not occur. Rust also _relies_ on this constraint to optimize codes. For example, a value behind a shared reference can be safely cached in a register for the lifetime of that reference.
+* The exclusive reference constraint is quite strong. Rust uses it to ensure that data races do not occur. Rust also _relies_ on this constraint to optimize code. For example, a value behind a shared reference can be safely cached in a register for the lifetime of that reference.
* The borrow checker is designed to accommodate many common patterns, such as taking exclusive references to different fields in a struct at the same time. But, there are some situations where it doesn't quite "get it" and this often results in "fighting with the borrow checker."
diff --git a/src/borrowing/shared.md b/src/borrowing/shared.md
index 87fb5e2f..f21ed4a7 100644
--- a/src/borrowing/shared.md
+++ b/src/borrowing/shared.md
@@ -62,4 +62,4 @@ Notes on stack returns:
-[Playground]: https://play.rust-lang.org/
+[Playground]: https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=0cb13be1c05d7e3446686ad9947c4671
diff --git a/src/control-flow-basics/blocks-and-scopes.md b/src/control-flow-basics/blocks-and-scopes.md
index 0cbb8ca8..b9658898 100644
--- a/src/control-flow-basics/blocks-and-scopes.md
+++ b/src/control-flow-basics/blocks-and-scopes.md
@@ -50,7 +50,7 @@ fn main() {
* You can show how the value of the block changes by changing the last line in the block. For instance, adding/removing a semicolon or using a `return`.
-* Show that a variable's scope is limited by adding a b` in the inner block in the last example, and then trying to access it outside that block.
+* Show that a variable's scope is limited by adding a `b` in the inner block in the last example, and then trying to access it outside that block.
* Shadowing is different from mutation, because after shadowing both variable's memory locations exist at the same time. Both are available under the same name, depending where you use it in the code.
* A shadowing variable can have a different type.
* Shadowing looks obscure at first, but is convenient for holding on to values after `.unwrap()`.
diff --git a/src/memory-management/exercise.rs b/src/memory-management/exercise.rs
index 60b3f674..4ca3160d 100644
--- a/src/memory-management/exercise.rs
+++ b/src/memory-management/exercise.rs
@@ -112,12 +112,12 @@ impl PackageBuilder {
fn main() {
let base64 = PackageBuilder::new("base64").version("0.13").build();
println!("base64: {base64:?}");
- let log = PackageBuilder::new("base64")
+ let log = PackageBuilder::new("log")
.version("0.4")
.language(Language::Rust)
.build();
println!("log: {log:?}");
- let serde = PackageBuilder::new("hawk")
+ let serde = PackageBuilder::new("serde")
.authors(vec!["djmitche".into()])
.version(String::from("4.0"))
.dependency(base64.as_dependency())
diff --git a/src/methods-and-traits/methods.md b/src/methods-and-traits/methods.md
index 92a1f22b..702d40c5 100644
--- a/src/methods-and-traits/methods.md
+++ b/src/methods-and-traits/methods.md
@@ -75,6 +75,5 @@ Key Points:
* Note how `self` is used like other structs and dot notation can be used to refer to individual fields.
* This might be a good time to demonstrate how the `&self` differs from `self` by trying to run `finish` twice.
* Beyond variants on `self`, there are also [special wrapper types](https://doc.rust-lang.org/reference/special-types-and-traits.html) allowed to be receiver types, such as `Box`.
-* Note that references have not been covered yet. References in method receivers are a particularly "natural" form of reference, so there is no need to go into a great level of detail.
diff --git a/src/pattern-matching/let-control-flow.md b/src/pattern-matching/let-control-flow.md
index aa381d5f..67407a25 100644
--- a/src/pattern-matching/let-control-flow.md
+++ b/src/pattern-matching/let-control-flow.md
@@ -33,6 +33,7 @@ fn main() {
}
```
+# `let else` expressions
For the common case of matching a pattern and returning from the function, use
[`let
else`](https://doc.rust-lang.org/rust-by-example/flow_control/let_else.html).
diff --git a/src/running-the-course.md b/src/running-the-course.md
index 3c931b1e..b0723d80 100644
--- a/src/running-the-course.md
+++ b/src/running-the-course.md
@@ -18,7 +18,7 @@ Before you run the course, you will want to:
popup (click the link with a little arrow next to "Speaker Notes"). This way
you have a clean screen to present to the class.
-1. Decide on the dates. Since the course takes at least three full days, we recommend that you
+1. Decide on the dates. Since the course takes four days, we recommend that you
schedule the days over two weeks. Course participants have said that
they find it helpful to have a gap in the course since it helps them process
all the information we give them.
diff --git a/src/running-the-course/course-structure.md b/src/running-the-course/course-structure.md
index b33529b6..60bda8d6 100644
--- a/src/running-the-course/course-structure.md
+++ b/src/running-the-course/course-structure.md
@@ -4,14 +4,14 @@
## Rust Fundamentals
-The first three days make up [Rust Fundaments](../welcome-day-1.md).
+The first four days make up [Rust Fundaments](../welcome-day-1.md).
The days are fast paced and we cover a lot of ground!
{{%course outline Fundamentals}}
## Deep Dives
-In addition to the 3-day class on Rust Fundamentals, we cover some more
+In addition to the 4-day class on Rust Fundamentals, we cover some more
specialized topics:
### Rust in Android
diff --git a/src/slices-and-lifetimes/lifetime-elision.md b/src/slices-and-lifetimes/lifetime-elision.md
index d3d72dfa..38b7c0f7 100644
--- a/src/slices-and-lifetimes/lifetime-elision.md
+++ b/src/slices-and-lifetimes/lifetime-elision.md
@@ -57,7 +57,7 @@ The `nearest` function provides another example of a function with multiple refe
Try adjusting the signature to "lie" about the lifetimes returned:
```rust,ignore
-fn nearest<'a>(points: &'a [Point], query: &'q Point) -> Option<&'q Point> {
+fn nearest<'a, 'q'>(points: &'a [Point], query: &'q Point) -> Option<&'q Point> {
```
This won't compile, demonstrating that the annotations are checked for validity
diff --git a/src/slices-and-lifetimes/slices.md b/src/slices-and-lifetimes/slices.md
index 59ed3deb..d506a431 100644
--- a/src/slices-and-lifetimes/slices.md
+++ b/src/slices-and-lifetimes/slices.md
@@ -37,6 +37,6 @@ fn main() {
* The question about modifying `a[3]` can spark an interesting discussion, but the answer is that for memory safety reasons
you cannot do it through `a` at this point in the execution, but you can read the data from both `a` and `s` safely.
- It works before you created the slice, and again after the `println`, when the slice is no longer used. More details will be explained in the borrow checker section.
+ It works before you created the slice, and again after the `println`, when the slice is no longer used.
diff --git a/src/smart-pointers/rc.md b/src/smart-pointers/rc.md
index c6c5ce4c..912927c7 100644
--- a/src/smart-pointers/rc.md
+++ b/src/smart-pointers/rc.md
@@ -37,6 +37,6 @@ fn main() {
* Use `Rc::strong_count` to check the reference count.
* `Rc::downgrade` gives you a *weakly reference-counted* object to
create cycles that will be dropped properly (likely in combination with
- `RefCell`, on the next slide).
+ `RefCell`).
diff --git a/src/std-traits/casting.md b/src/std-traits/casting.md
index 7b9cacfa..896b071f 100644
--- a/src/std-traits/casting.md
+++ b/src/std-traits/casting.md
@@ -20,6 +20,11 @@ The results of `as` are _always_ defined in Rust and consistent across
platforms. This might not match your intuition for changing sign or casting to
a smaller type -- check the docs, and comment for clarity.
+Casting with `as` is a relatively sharp tool that is easy to use incorrectly, and can be a source of subtle bugs as future maintenance work changes the types that are used or the ranges of values in types. Casts are best used only when the intent is to indicate unconditional truncation (e.g. selecting the bottom 32 bits of a `u64` with `as u32`, regardless of what was in the high bits).
+
+For infallible casts (e.g. `u32` to `u64`), prefer using `From` or `Into` over `as`
+to confirm that the cast is in fact infallible. For fallible casts, `TryFrom` and `TryInto` are available when you want to handle casts that fit differently from those that don't.
+
Consider taking a break after this slide.
diff --git a/src/std-traits/default.md b/src/std-traits/default.md
index 50404295..b4ff71fd 100644
--- a/src/std-traits/default.md
+++ b/src/std-traits/default.md
@@ -45,9 +45,9 @@ fn main() {
* A derived implementation will produce a value where all fields are set to their default values.
* This means all types in the struct must implement `Default` too.
* Standard Rust types often implement `Default` with reasonable values (e.g. `0`, `""`, etc).
- * The partial struct copy works nicely with default.
- * Rust standard library is aware that types can implement `Default` and provides convenience methods that use it.
- * the `..` syntax is called [struct update syntax][2]
+ * The partial struct initialization works nicely with default.
+ * The Rust standard library is aware that types can implement `Default` and provides convenience methods that use it.
+ * The `..` syntax is called [struct update syntax][2].
diff --git a/src/std-traits/from-and-into.md b/src/std-traits/from-and-into.md
index dd4ae235..57af2244 100644
--- a/src/std-traits/from-and-into.md
+++ b/src/std-traits/from-and-into.md
@@ -11,7 +11,7 @@ fn main() {
let s = String::from("hello");
let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);
let one = i16::from(true);
- let bigger = i32::from(123i16);
+ let bigger = i32::from(123_i16);
println!("{s}, {addr}, {one}, {bigger}");
}
```
@@ -23,7 +23,7 @@ fn main() {
let s: String = "hello".into();
let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();
let one: i16 = true.into();
- let bigger: i32 = 123i16.into();
+ let bigger: i32 = 123_i16.into();
println!("{s}, {addr}, {one}, {bigger}");
}
```
diff --git a/src/user-defined-types/enums.md b/src/user-defined-types/enums.md
index 7dd08ae3..425be9e6 100644
--- a/src/user-defined-types/enums.md
+++ b/src/user-defined-types/enums.md
@@ -68,9 +68,6 @@ Key Points:
Rust has several optimizations it can employ to make enums take up less space.
- * Niche optimization: Rust will merge unused bit patterns for the enum
- discriminant.
-
* Null pointer optimization: For [some
types](https://doc.rust-lang.org/std/option/#representation), Rust guarantees
that `size_of::()` equals `size_of::