1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-07-09 15:55:36 +02:00

Format all Markdown files with dprint ()

This is the result of running `dprint fmt` after removing `src/` from
the list of excluded directories.

This also reformats the Rust code: we might want to tweak this a bit in
the future since some of the changes removes the hand-formatting. Of
course, this formatting can be seen as a mis-feature, so maybe this is
good overall.

Thanks to mdbook-i18n-helpers 0.2, the POT file is nearly unchanged
after this, meaning that all existing translations remain valid! A few
messages were changed because of stray whitespace characters:

     msgid ""
     "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."
     msgstr ""

The formatting is enforced in CI and we will have to see how annoying
this is in practice for the many contributors. If it becomes annoying,
we should look into fixing  so that `dprint` can annotate
the lines that need fixing directly, then I think we can consider more
strict formatting checks.

I added more customization to `rustfmt.toml`. This is to better emulate
the dense style used in the course:

- `max_width = 85` allows lines to take up the full width available in
our code blocks (when taking margins and the line numbers into account).
- `wrap_comments = true` ensures that we don't show very long comments
in the code examples. I edited some comments to shorten them and avoid
unnecessary line breaks — please trim other unnecessarily long comments
when you see them! Remember we're writing code for slides 😄
- `use_small_heuristics = "Max"` allows for things like struct literals
and if-statements to take up the full line width configured above.

The formatting settings apply to all our Rust code right now — I think
we could improve this with https://github.com/dprint/dprint/issues/711
which lets us add per-directory `dprint` configuration files. However,
the `inherit: true` setting is not yet implemented (as far as I can
tell), so a nested configuration file will have to copy most or all of
the top-level file.
This commit is contained in:
Martin Geisler
2023-12-31 00:15:07 +01:00
committed by GitHub
parent f43e72e0ad
commit c9f66fd425
302 changed files with 3067 additions and 2622 deletions
dprint.json
mdbook-course/src
mdbook-exerciser/src
po
rustfmt.toml
src
SUMMARY.mdandroid.md
android
async.md
async
bare-metal.md
bare-metal
borrowing
cargo.md
cargo
chromium.md
chromium
concurrency.md
concurrency
control-flow-basics
credits.md
error-handling
exercises
generics
glossary.md
hello-world
index.md
iterators
memory-management
methods-and-traits
modules
other-resources.md
pattern-matching
references
running-the-course.md
running-the-course
slices-and-lifetimes
smart-pointers
std-traits
std-types
testing
thanks.md
tuples-and-arrays
types-and-values
unsafe-rust
user-defined-types
welcome-day-1.mdwelcome-day-2.mdwelcome-day-3.mdwelcome-day-4.md

@ -6,9 +6,13 @@ minutes: 5
Rust comes with extensive documentation. For example:
* All of the details about [loops](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html).
* Primitive types like [`u8`](https://doc.rust-lang.org/stable/std/primitive.u8.html).
* Standard library types like [`Option`](https://doc.rust-lang.org/stable/std/option/enum.Option.html) or [`BinaryHeap`](https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html).
- All of the details about
[loops](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html).
- Primitive types like
[`u8`](https://doc.rust-lang.org/stable/std/primitive.u8.html).
- Standard library types like
[`Option`](https://doc.rust-lang.org/stable/std/option/enum.Option.html) or
[`BinaryHeap`](https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html).
In fact, you can document your own code:
@ -29,8 +33,8 @@ automatically documented at [`docs.rs`](https://docs.rs) using the
[rustdoc](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html) tool. It is
idiomatic to document all public items in an API using this pattern.
To document an item from inside the item (such as inside a module), use `//!`
or `/*! .. */`, called "inner doc comments":
To document an item from inside the item (such as inside a module), use `//!` or
`/*! .. */`, called "inner doc comments":
```rust,editable
//! This module contains functionality relating to divisibility of integers.
@ -38,7 +42,7 @@ or `/*! .. */`, called "inner doc comments":
<details>
* Show students the generated docs for the `rand` crate at
- Show students the generated docs for the `rand` crate at
<https://docs.rs/rand>.
</details>

@ -25,9 +25,7 @@ struct Counter<T: Eq + Hash> {
impl<T: Eq + Hash> Counter<T> {
/// Create a new Counter.
fn new() -> Self {
Counter {
values: HashMap::new(),
}
Counter { values: HashMap::new() }
}
/// Count an occurrence of the given value.

@ -16,14 +16,16 @@ fn main() {
page_counts.insert("Pride and Prejudice".to_string(), 303);
if !page_counts.contains_key("Les Misérables") {
println!("We know about {} books, but not Les Misérables.",
page_counts.len());
println!(
"We know about {} books, but not Les Misérables.",
page_counts.len()
);
}
for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
match page_counts.get(book) {
Some(count) => println!("{book}: {count} pages"),
None => println!("{book} is unknown.")
None => println!("{book} is unknown."),
}
}
@ -39,33 +41,42 @@ fn main() {
<details>
* `HashMap` is not defined in the prelude and needs to be brought into scope.
* Try the following lines of code. The first line will see if a book is in the hashmap and if not return an alternative value. The second line will insert the alternative value in the hashmap if the book is not found.
- `HashMap` is not defined in the prelude and needs to be brought into scope.
- Try the following lines of code. The first line will see if a book is in the
hashmap and if not return an alternative value. The second line will insert
the alternative value in the hashmap if the book is not found.
```rust,ignore
let pc1 = page_counts
.get("Harry Potter and the Sorcerer's Stone")
.unwrap_or(&336);
let pc2 = page_counts
.entry("The Hunger Games".to_string())
.or_insert(374);
```
* Unlike `vec!`, there is unfortunately no standard `hashmap!` macro.
* Although, since Rust 1.56, HashMap implements [`From<[(K, V); N]>`][1], which allows us to easily initialize a hash map from a literal array:
```rust,ignore
let pc1 = page_counts
.get("Harry Potter and the Sorcerer's Stone")
.unwrap_or(&336);
let pc2 = page_counts
.entry("The Hunger Games".to_string())
.or_insert(374);
```
- Unlike `vec!`, there is unfortunately no standard `hashmap!` macro.
- Although, since Rust 1.56, HashMap implements [`From<[(K, V); N]>`][1],
which allows us to easily initialize a hash map from a literal array:
```rust,ignore
let page_counts = HashMap::from([
("Harry Potter and the Sorcerer's Stone".to_string(), 336),
("The Hunger Games".to_string(), 374),
]);
```
```rust,ignore
let page_counts = HashMap::from([
("Harry Potter and the Sorcerer's Stone".to_string(), 336),
("The Hunger Games".to_string(), 374),
]);
```
* 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?
- 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 the Rust docs. Show students the docs for this type, and the helpful link back to the `keys` method.
- This type has several "method-specific" return types, such as
`std::collections::hash_map::Keys`. These types often appear in searches of
the Rust docs. Show students the docs for this type, and the helpful link back
to the `keys` method.
[1]: https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#impl-From%3C%5B(K,+V);+N%5D%3E-for-HashMap%3CK,+V,+RandomState%3E

@ -4,8 +4,8 @@ minutes: 10
# Option
We have already seen some use of `Option<T>`. It stores either a
value of type `T` or nothing. For example,
We have already seen some use of `Option<T>`. It stores either a value of type
`T` or nothing. For example,
[`String::find`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.find)
returns an `Option<usize>`.
@ -23,10 +23,14 @@ fn main() {
<details>
* `Option` is widely used, not just in the standard library.
* `unwrap` will return the value in an `Option`, or panic. `expect` is similar but takes an error message.
* You can panic on None, but you can't "accidentally" forget to check for None.
* It's common to `unwrap`/`expect` all over the place when hacking something together, but production code typically handles `None` in a nicer fashion.
* The niche optimization means that `Option<T>` often has the same size in memory as `T`.
- `Option` is widely used, not just in the standard library.
- `unwrap` will return the value in an `Option`, or panic. `expect` is similar
but takes an error message.
- You can panic on None, but you can't "accidentally" forget to check for
None.
- It's common to `unwrap`/`expect` all over the place when hacking something
together, but production code typically handles `None` in a nicer fashion.
- The niche optimization means that `Option<T>` often has the same size in
memory as `T`.
</details>

@ -5,9 +5,9 @@ minutes: 10
# Result
`Result` is similar to `Option`, but indicates the success or failure of an
operation, each with a different type. This is similar to the `Res` defined
in the expression exercise, but generic: `Result<T, E>` where `T` is used in
the `Ok` variant and `E` appears in the `Err` variant.
operation, each with a different type. This is similar to the `Res` defined in
the expression exercise, but generic: `Result<T, E>` where `T` is used in the
`Ok` variant and `E` appears in the `Err` variant.
```rust,editable
use std::fs::File;
@ -23,7 +23,7 @@ fn main() {
} else {
println!("Could not read file content");
}
},
}
Err(err) => {
println!("The diary could not be opened: {err}");
}
@ -33,11 +33,14 @@ fn main() {
<details>
* As with `Option`, the successful value sits inside of `Result`, forcing the developer to
explicitly extract it. This encourages error checking. In the case where an error should never happen,
`unwrap()` or `expect()` can be called, and this is a signal of the developer intent too.
* `Result` documentation is a recommended read. Not during the course, but it is worth mentioning.
It contains a lot of convenience methods and functions that help functional-style programming.
* `Result` is the standard type to implement error handling as we will see on Day 3.
- As with `Option`, the successful value sits inside of `Result`, forcing the
developer to explicitly extract it. This encourages error checking. In the
case where an error should never happen, `unwrap()` or `expect()` can be
called, and this is a signal of the developer intent too.
- `Result` documentation is a recommended read. Not during the course, but it is
worth mentioning. It contains a lot of convenience methods and functions that
help functional-style programming.
- `Result` is the standard type to implement error handling as we will see on
Day 3.
</details>

@ -8,8 +8,11 @@ Rust comes with a standard library which helps establish a set of common types
used by Rust libraries and programs. This way, two libraries can work together
smoothly because they both use the same `String` type.
In fact, Rust contains several layers of the Standard Library: `core`, `alloc` and `std`.
* `core` includes the most basic types and functions that don't depend on `libc`, allocator or
even the presence of an operating system.
* `alloc` includes types which require a global heap allocator, such as `Vec`, `Box` and `Arc`.
* Embedded Rust applications often only use `core`, and sometimes `alloc`.
In fact, Rust contains several layers of the Standard Library: `core`, `alloc`
and `std`.
- `core` includes the most basic types and functions that don't depend on
`libc`, allocator or even the presence of an operating system.
- `alloc` includes types which require a global heap allocator, such as `Vec`,
`Box` and `Arc`.
- Embedded Rust applications often only use `core`, and sometimes `alloc`.

@ -18,30 +18,41 @@ fn main() {
println!("s2: len = {}, capacity = {}", s2.len(), s2.capacity());
let s3 = String::from("🇨🇭");
println!("s3: len = {}, number of chars = {}", s3.len(),
s3.chars().count());
println!("s3: len = {}, number of chars = {}", s3.len(), s3.chars().count());
}
```
`String` implements [`Deref<Target = str>`][2], which means that you can call all
`str` methods on a `String`.
`String` implements [`Deref<Target = str>`][2], which means that you can call
all `str` methods on a `String`.
[1]: https://doc.rust-lang.org/std/string/struct.String.html
[2]: https://doc.rust-lang.org/std/string/struct.String.html#deref-methods-str
<details>
* `String::new` returns a new empty string, use `String::with_capacity` when you know how much data you want to push to the string.
* `String::len` returns the size of the `String` in bytes (which can be different from its length in characters).
* `String::chars` returns an iterator over the actual characters. Note that a `char` can be different from what a human will consider a "character" due to [grapheme clusters](https://docs.rs/unicode-segmentation/latest/unicode_segmentation/struct.Graphemes.html).
* When people refer to strings they could either be talking about `&str` or `String`.
* When a type implements `Deref<Target = T>`, the compiler will let you transparently call methods from `T`.
* We haven't discussed the `Deref` trait yet, so at this point this mostly explains the structure of the sidebar in the documentation.
* `String` implements `Deref<Target = str>` which transparently gives it access to `str`'s methods.
* Write and compare `let s3 = s1.deref();` and `let s3 = &*s1;`.
* `String` is implemented as a wrapper around a vector of bytes, many of the operations you see supported on vectors are also supported on `String`, but with some extra guarantees.
* Compare the different ways to index a `String`:
* To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, out-of-bounds.
* To a substring by using `s3[0..4]`, where that slice is on character boundaries or not.
- `String::new` returns a new empty string, use `String::with_capacity` when you
know how much data you want to push to the string.
- `String::len` returns the size of the `String` in bytes (which can be
different from its length in characters).
- `String::chars` returns an iterator over the actual characters. Note that a
`char` can be different from what a human will consider a "character" due to
[grapheme clusters](https://docs.rs/unicode-segmentation/latest/unicode_segmentation/struct.Graphemes.html).
- When people refer to strings they could either be talking about `&str` or
`String`.
- When a type implements `Deref<Target = T>`, the compiler will let you
transparently call methods from `T`.
- We haven't discussed the `Deref` trait yet, so at this point this mostly
explains the structure of the sidebar in the documentation.
- `String` implements `Deref<Target = str>` which transparently gives it
access to `str`'s methods.
- Write and compare `let s3 = s1.deref();` and `let s3 = &*s1;`.
- `String` is implemented as a wrapper around a vector of bytes, many of the
operations you see supported on vectors are also supported on `String`, but
with some extra guarantees.
- Compare the different ways to index a `String`:
- To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound,
out-of-bounds.
- To a substring by using `s3[0..4]`, where that slice is on character
boundaries or not.
</details>

@ -38,16 +38,18 @@ methods on a `Vec`.
<details>
* `Vec` is a type of collection, along with `String` and `HashMap`. The data it contains is stored
on the heap. This means the amount of data doesn't need to be known at compile time. It can grow
or shrink at runtime.
* Notice how `Vec<T>` is a generic type too, but you don't have to specify `T` explicitly. As always
with Rust type inference, the `T` was established during the first `push` call.
* `vec![...]` is a canonical macro to use instead of `Vec::new()` and it supports adding initial
elements to the vector.
* To index the vector you use `[` `]`, but they will panic if out of bounds. Alternatively, using
`get` will return an `Option`. The `pop` function will remove the last element.
* Slices are covered on day 3. For now, students only need to know that a value
- `Vec` is a type of collection, along with `String` and `HashMap`. The data it
contains is stored on the heap. This means the amount of data doesn't need to
be known at compile time. It can grow or shrink at runtime.
- Notice how `Vec<T>` is a generic type too, but you don't have to specify `T`
explicitly. As always with Rust type inference, the `T` was established during
the first `push` call.
- `vec![...]` is a canonical macro to use instead of `Vec::new()` and it
supports adding initial elements to the vector.
- To index the vector you use `[` `]`, but they will panic if out of bounds.
Alternatively, using `get` will return an `Option`. The `pop` function will
remove the last element.
- Slices are covered on day 3. For now, students only need to know that a value
of type `Vec` gives access to all of the documented slice methods, too.
</details>