Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellaneous minor improvements #2370

Merged
merged 12 commits into from
Sep 20, 2024
12 changes: 12 additions & 0 deletions src/control-flow-basics/break-continue/labels.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ fn main() {
print!("elements searched: {elements_searched}");
}
```

<details>

- Labeled break also works on arbitrary blocks, e.g.
```rust
'label: {
break 'label;
println!("This line gets skipped");
}
```

</details>
randomPoison marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions src/control-flow-basics/exercise.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ initial `n`.
todo!("Implement this")
}

{{#include exercise.rs:tests}}

{{#include exercise.rs:main}}
todo!("Implement this")
}
```
2 changes: 1 addition & 1 deletion src/control-flow-basics/exercise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ fn test_collatz_length() {

// ANCHOR: main
fn main() {
// ANCHOR_END: main
println!("Length: {}", collatz_length(11));
}
// ANCHOR_END: main
13 changes: 12 additions & 1 deletion src/generics/generic-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ fn pick<T>(n: i32, even: T, odd: T) -> T {

fn main() {
println!("picked a number: {:?}", pick(97, 222, 333));
println!("picked a tuple: {:?}", pick(28, ("dog", 1), ("cat", 2)));
println!("picked a string: {:?}", pick(28, "dog", "cat"));
}
```

<details>

- Rust infers a type for T based on the types of the arguments and return value.

- In this example we only use the primitive types `i32` and `&str` for `T`, but
we can use any type here, including user-defined types:

```rust,ignore
struct Foo {
val: u8,
}

pick(123, Foo { val: 7 }, Foo { val: 456 });
```

- This is similar to C++ templates, but Rust partially compiles the generic
function immediately, so that function must be valid for all types matching
the constraints. For example, try modifying `pick` to return `even + odd` if
Expand Down
18 changes: 6 additions & 12 deletions src/methods-and-traits/exercise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,18 @@

// ANCHOR: solution
// ANCHOR: setup
use std::fmt::Display;

pub trait Logger {
/// Log a message at the given verbosity level.
fn log(&self, verbosity: u8, message: impl Display);
randomPoison marked this conversation as resolved.
Show resolved Hide resolved
fn log(&self, verbosity: u8, message: &str);
}

struct StderrLogger;

impl Logger for StderrLogger {
fn log(&self, verbosity: u8, message: impl Display) {
fn log(&self, verbosity: u8, message: &str) {
eprintln!("verbosity={verbosity}: {message}");
}
}

fn do_things(logger: &impl Logger) {
logger.log(5, "FYI");
logger.log(2, "Uhoh");
}
// ANCHOR_END: setup

/// Only log messages up to the given verbosity level.
Expand All @@ -42,7 +35,7 @@ struct VerbosityFilter {
}

impl Logger for VerbosityFilter {
fn log(&self, verbosity: u8, message: impl Display) {
fn log(&self, verbosity: u8, message: &str) {
if verbosity <= self.max_verbosity {
self.inner.log(verbosity, message);
}
Expand All @@ -51,7 +44,8 @@ impl Logger for VerbosityFilter {

// ANCHOR: main
fn main() {
let l = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
do_things(&l);
let logger = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
logger.log(5, "FYI");
logger.log(2, "Uhoh");
}
// ANCHOR_END: main
7 changes: 0 additions & 7 deletions src/pattern-matching/destructuring-enums.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,5 @@ arm, `half` is bound to the value inside the `Ok` variant. In the second arm,
matched.
- Demonstrate what happens when the search is inexhaustive. Note the advantage
the Rust compiler provides by confirming when all cases are handled.
- Save the result of `divide_in_two` in the `result` variable and `match` it in
a loop. That won't compile because `msg` is consumed when matched. To fix it,
match `&result` instead of `result`. That will make `msg` a reference so it
won't be consumed. This
["match ergonomics"](https://rust-lang.github.io/rfcs/2005-match-ergonomics.html)
appeared in Rust 2018. If you want to support older Rust, replace `msg` with
`ref msg` in the pattern.

</details>
4 changes: 2 additions & 2 deletions src/pattern-matching/exercise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ fn eval(e: Expression) -> Result<i64, String> {
Expression::Op { op, left, right } => {
let left = match eval(*left) {
Ok(v) => v,
e @ Err(_) => return e,
Err(e) => return Err(e),
};
let right = match eval(*right) {
Ok(v) => v,
e @ Err(_) => return e,
Err(e) => return Err(e),
randomPoison marked this conversation as resolved.
Show resolved Hide resolved
};
Ok(match op {
Operation::Add => left + right,
Expand Down
20 changes: 20 additions & 0 deletions src/pattern-matching/match.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,24 @@ Key Points:
- The condition defined in the guard applies to every expression in a pattern
with an `|`.

# More To Explore

- Another piece of pattern syntax you can show students is the `@` syntax which
binds a part of a pattern to a variable. For example:

```rust
let opt = Some(123);
match opt {
outer @ Some(inner) => {
println!("outer: {outer:?}, inner: {inner}");
}
None => {}
}
```

In this example `inner` has the value 123 which it pulled from the `Option`
via destructuring, `outer` captures the entire `Some(inner)` expression, so it
contains the full `Option::Some(123)`. This is rarely used but can be useful
in more complex patterns.

</details>
2 changes: 2 additions & 0 deletions src/references/shared.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ fn x_axis(x: &i32) -> &(i32, i32) {

<details>

- References can never be null in Rust, so null checking is not necessary.

- A reference is said to "borrow" the value it refers to, and this is a good
model for students not familiar with pointers: code can use the reference to
access the value, but is still "owned" by the original variable. The course
Expand Down
7 changes: 0 additions & 7 deletions src/references/slices.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ fn main() {
```

- Slices borrow data from the sliced type.
- Question: What happens if you modify `a[3]` right before printing `s`?

<details>

Expand All @@ -43,10 +42,4 @@ fn main() {
- Slices always borrow from another object. In this example, `a` has to remain
'alive' (in scope) for at least as long as our slice.

- 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.

</details>
5 changes: 0 additions & 5 deletions src/std-types/hashmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ fn main() {

- Alternatively HashMap can be built from any `Iterator` which yields key-value
tuples.
- We are showing `HashMap<String, i32>`, and avoid using `&str` as key to make
examples easier. Using references in collections can, of course, be done, but
it can lead into complications with the borrow checker.
- Try removing `to_string()` from the example above and see if it still
compiles. Where do you think we might run into issues?

- This type has several "method-specific" return types, such as
`std::collections::hash_map::Keys`. These types often appear in searches of
Expand Down