1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-14 22:56:44 +02:00

Miscellaneous minor improvements (#2370)

This commit is contained in:
Nicole L 2024-09-20 14:19:53 -07:00 committed by GitHub
parent aeb643f380
commit 2f9babd098
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 57 additions and 37 deletions

View File

@ -19,3 +19,15 @@ fn main() {
print!("elements searched: {elements_searched}"); 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>

View File

@ -30,7 +30,7 @@ initial `n`.
todo!("Implement this") todo!("Implement this")
} }
{{#include exercise.rs:tests}}
{{#include exercise.rs:main}} {{#include exercise.rs:main}}
todo!("Implement this")
}
``` ```

View File

@ -34,6 +34,6 @@ fn test_collatz_length() {
// ANCHOR: main // ANCHOR: main
fn main() { fn main() {
// ANCHOR_END: main
println!("Length: {}", collatz_length(11)); println!("Length: {}", collatz_length(11));
} }
// ANCHOR_END: main

View File

@ -19,7 +19,7 @@ fn pick<T>(n: i32, even: T, odd: T) -> T {
fn main() { fn main() {
println!("picked a number: {:?}", pick(97, 222, 333)); 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"));
} }
``` ```
@ -27,6 +27,17 @@ fn main() {
- Rust infers a type for T based on the types of the arguments and return value. - 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 - This is similar to C++ templates, but Rust partially compiles the generic
function immediately, so that function must be valid for all types matching function immediately, so that function must be valid for all types matching
the constraints. For example, try modifying `pick` to return `even + odd` if the constraints. For example, try modifying `pick` to return `even + odd` if

View File

@ -14,25 +14,18 @@
// ANCHOR: solution // ANCHOR: solution
// ANCHOR: setup // ANCHOR: setup
use std::fmt::Display;
pub trait Logger { pub trait Logger {
/// Log a message at the given verbosity level. /// Log a message at the given verbosity level.
fn log(&self, verbosity: u8, message: impl Display); fn log(&self, verbosity: u8, message: &str);
} }
struct StderrLogger; struct StderrLogger;
impl Logger for StderrLogger { impl Logger for StderrLogger {
fn log(&self, verbosity: u8, message: impl Display) { fn log(&self, verbosity: u8, message: &str) {
eprintln!("verbosity={verbosity}: {message}"); eprintln!("verbosity={verbosity}: {message}");
} }
} }
fn do_things(logger: &impl Logger) {
logger.log(5, "FYI");
logger.log(2, "Uhoh");
}
// ANCHOR_END: setup // ANCHOR_END: setup
/// Only log messages up to the given verbosity level. /// Only log messages up to the given verbosity level.
@ -42,7 +35,7 @@ struct VerbosityFilter {
} }
impl Logger for 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 { if verbosity <= self.max_verbosity {
self.inner.log(verbosity, message); self.inner.log(verbosity, message);
} }
@ -51,7 +44,8 @@ impl Logger for VerbosityFilter {
// ANCHOR: main // ANCHOR: main
fn main() { fn main() {
let l = VerbosityFilter { max_verbosity: 3, inner: StderrLogger }; let logger = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
do_things(&l); logger.log(5, "FYI");
logger.log(2, "Uhoh");
} }
// ANCHOR_END: main // ANCHOR_END: main

View File

@ -47,12 +47,5 @@ arm, `half` is bound to the value inside the `Ok` variant. In the second arm,
matched. matched.
- Demonstrate what happens when the search is inexhaustive. Note the advantage - Demonstrate what happens when the search is inexhaustive. Note the advantage
the Rust compiler provides by confirming when all cases are handled. 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> </details>

View File

@ -44,11 +44,11 @@ fn eval(e: Expression) -> Result<i64, String> {
Expression::Op { op, left, right } => { Expression::Op { op, left, right } => {
let left = match eval(*left) { let left = match eval(*left) {
Ok(v) => v, Ok(v) => v,
e @ Err(_) => return e, Err(e) => return Err(e),
}; };
let right = match eval(*right) { let right = match eval(*right) {
Ok(v) => v, Ok(v) => v,
e @ Err(_) => return e, Err(e) => return Err(e),
}; };
Ok(match op { Ok(match op {
Operation::Add => left + right, Operation::Add => left + right,

View File

@ -56,4 +56,24 @@ Key Points:
- The condition defined in the guard applies to every expression in a pattern - The condition defined in the guard applies to every expression in a pattern
with an `|`. 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> </details>

View File

@ -38,6 +38,8 @@ fn x_axis(x: &i32) -> &(i32, i32) {
<details> <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 - 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 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 access the value, but is still "owned" by the original variable. The course

View File

@ -20,7 +20,6 @@ fn main() {
``` ```
- Slices borrow data from the sliced type. - Slices borrow data from the sliced type.
- Question: What happens if you modify `a[3]` right before printing `s`?
<details> <details>
@ -43,10 +42,4 @@ fn main() {
- Slices always borrow from another object. In this example, `a` has to remain - Slices always borrow from another object. In this example, `a` has to remain
'alive' (in scope) for at least as long as our slice. '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> </details>

View File

@ -67,11 +67,6 @@ fn main() {
- Alternatively HashMap can be built from any `Iterator` which yields key-value - Alternatively HashMap can be built from any `Iterator` which yields key-value
tuples. 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 - This type has several "method-specific" return types, such as
`std::collections::hash_map::Keys`. These types often appear in searches of `std::collections::hash_map::Keys`. These types often appear in searches of