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:
parent
aeb643f380
commit
2f9babd098
@ -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>
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user