1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-06-17 22:57:35 +02:00

Format all Markdown files with dprint (#1157)

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 dprint/check#11 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

View File

@ -2,10 +2,10 @@
To practice your Async Rust skills, we have again two exercises for you:
* Dining philosophers: we already saw this problem in the morning. This time
you are going to implement it with Async Rust.
- Dining philosophers: we already saw this problem in the morning. This time you
are going to implement it with Async Rust.
* A Broadcast Chat Application: this is a larger project that allows you
- A Broadcast Chat Application: this is a larger project that allows you
experiment with more advanced Async Rust features.
<details>

View File

@ -1,14 +1,13 @@
# Broadcast Chat Application
In this exercise, we want to use our new knowledge to implement a broadcast
chat application. We have a chat server that the clients connect to and publish
their messages. The client reads user messages from the standard input, and
sends them to the server. The chat server broadcasts each message that it
receives to all the clients.
In this exercise, we want to use our new knowledge to implement a broadcast chat
application. We have a chat server that the clients connect to and publish their
messages. The client reads user messages from the standard input, and sends them
to the server. The chat server broadcasts each message that it receives to all
the clients.
For this, we use [a broadcast channel][1] on the server, and
[`tokio_websockets`][2] for the communication between the client and the
server.
[`tokio_websockets`][2] for the communication between the client and the server.
Create a new Cargo project and add the following dependencies:
@ -21,31 +20,31 @@ _Cargo.toml_:
```
## The required APIs
You are going to need the following functions from `tokio` and
[`tokio_websockets`][2]. Spend a few minutes to familiarize yourself with the
API.
API.
- [StreamExt::next()][3] implemented by `WebSocketStream`: for asynchronously
reading messages from a Websocket Stream.
- [SinkExt::send()][4] implemented by `WebSocketStream`: for asynchronously
sending messages on a Websocket Stream.
- [Lines::next_line()][5]: for asynchronously reading user messages
from the standard input.
- [Lines::next_line()][5]: for asynchronously reading user messages from the
standard input.
- [Sender::subscribe()][6]: for subscribing to a broadcast channel.
## Two binaries
Normally in a Cargo project, you can have only one binary, and one
`src/main.rs` file. In this project, we need two binaries. One for the client,
and one for the server. You could potentially make them two separate Cargo
projects, but we are going to put them in a single Cargo project with two
binaries. For this to work, the client and the server code should go under
`src/bin` (see the [documentation][7]).
Normally in a Cargo project, you can have only one binary, and one `src/main.rs`
file. In this project, we need two binaries. One for the client, and one for the
server. You could potentially make them two separate Cargo projects, but we are
going to put them in a single Cargo project with two binaries. For this to work,
the client and the server code should go under `src/bin` (see the
[documentation][7]).
Copy the following server and client code into `src/bin/server.rs` and
`src/bin/client.rs`, respectively. Your task is to complete these files as
described below.
described below.
_src/bin/server.rs_:
@ -74,6 +73,7 @@ _src/bin/client.rs_:
```
## Running the binaries
Run the server with:
```shell
@ -88,16 +88,16 @@ cargo run --bin client
## Tasks
* Implement the `handle_connection` function in `src/bin/server.rs`.
* Hint: Use `tokio::select!` for concurrently performing two tasks in a
- Implement the `handle_connection` function in `src/bin/server.rs`.
- Hint: Use `tokio::select!` for concurrently performing two tasks in a
continuous loop. One task receives messages from the client and broadcasts
them. The other sends messages received by the server to the client.
* Complete the main function in `src/bin/client.rs`.
* Hint: As before, use `tokio::select!` in a continuous loop for concurrently
- Complete the main function in `src/bin/client.rs`.
- Hint: As before, use `tokio::select!` in a continuous loop for concurrently
performing two tasks: (1) reading user messages from standard input and
sending them to the server, and (2) receiving messages from the server, and
displaying them for the user.
* Optional: Once you are done, change the code to broadcast messages to all
- Optional: Once you are done, change the code to broadcast messages to all
clients, but the sender of the message.
[1]: https://docs.rs/tokio/latest/tokio/sync/broadcast/fn.channel.html

View File

@ -31,9 +31,7 @@ async fn handle_connection(
) -> Result<(), Box<dyn Error + Send + Sync>> {
// ANCHOR_END: handle_connection
ws_stream
.send(Message::text("Welcome to chat! Type a message".into()))
.await?;
ws_stream.send(Message::text("Welcome to chat! Type a message".into())).await?;
let mut bcast_rx = bcast_tx.subscribe();
// A continuous loop for concurrently performing two tasks: (1) receiving

View File

@ -4,9 +4,9 @@ See [dining philosophers](dining-philosophers.md) for a description of the
problem.
As before, you will need a local
[Cargo installation](../../cargo/running-locally.md) for this exercise. Copy
the code below to a file called `src/main.rs`, fill out the blanks, and test
that `cargo run` does not deadlock:
[Cargo installation](../../cargo/running-locally.md) for this exercise. Copy the
code below to a file called `src/main.rs`, fill out the blanks, and test that
`cargo run` does not deadlock:
<!-- File src/main.rs -->
@ -32,8 +32,8 @@ that `cargo run` does not deadlock:
}
```
Since this time you are using Async Rust, you'll need a `tokio` dependency.
You can use the following `Cargo.toml`:
Since this time you are using Async Rust, you'll need a `tokio` dependency. You
can use the following `Cargo.toml`:
<!-- File Cargo.toml -->
@ -44,14 +44,14 @@ version = "0.1.0"
edition = "2021"
[dependencies]
tokio = {version = "1.26.0", features = ["sync", "time", "macros", "rt-multi-thread"]}
tokio = { version = "1.26.0", features = ["sync", "time", "macros", "rt-multi-thread"] }
```
Also note that this time you have to use the `Mutex` and the `mpsc` module
from the `tokio` crate.
Also note that this time you have to use the `Mutex` and the `mpsc` module from
the `tokio` crate.
<details>
* Can you make your implementation single-threaded?
- Can you make your implementation single-threaded?
</details>

View File

@ -15,9 +15,9 @@
// ANCHOR: solution
// ANCHOR: Philosopher
use std::sync::Arc;
use tokio::time;
use tokio::sync::mpsc::{self, Sender};
use tokio::sync::Mutex;
use tokio::time;
struct Fork;
@ -33,7 +33,8 @@ struct Philosopher {
impl Philosopher {
async fn think(&self) {
self.thoughts
.send(format!("Eureka! {} has a new idea!", &self.name)).await
.send(format!("Eureka! {} has a new idea!", &self.name))
.await
.unwrap();
}
// ANCHOR_END: Philosopher-think
@ -78,7 +79,7 @@ async fn main() {
// To avoid a deadlock, we have to break the symmetry
// somewhere. This will swap the forks without deinitializing
// either of them.
if i == 0 {
if i == 0 {
std::mem::swap(&mut left_fork, &mut right_fork);
}
philosophers.push(Philosopher {
@ -100,7 +101,6 @@ async fn main() {
phil.eat().await;
}
});
}
// Output their thoughts

View File

@ -78,9 +78,9 @@ cargo run
## Tasks
* Use threads to check the links in parallel: send the URLs to be checked to a
- Use threads to check the links in parallel: send the URLs to be checked to a
channel and let a few threads check the URLs in parallel.
* Extend this to recursively extract links from all pages on the
- Extend this to recursively extract links from all pages on the
`www.google.org` domain. Put an upper limit of 100 pages or so so that you
don't end up being blocked by the site.

View File

@ -13,10 +13,12 @@
// limitations under the License.
// ANCHOR: solution
use std::{sync::mpsc, sync::Arc, sync::Mutex, thread};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
// ANCHOR: setup
use reqwest::{blocking::Client, Url};
use reqwest::blocking::Client;
use reqwest::Url;
use scraper::{Html, Selector};
use thiserror::Error;
@ -79,10 +81,7 @@ impl CrawlState {
fn new(start_url: &Url) -> CrawlState {
let mut visited_pages = std::collections::HashSet::new();
visited_pages.insert(start_url.as_str().to_string());
CrawlState {
domain: start_url.domain().unwrap().to_string(),
visited_pages,
}
CrawlState { domain: start_url.domain().unwrap().to_string(), visited_pages }
}
/// Determine whether links within the given page should be extracted.
@ -138,10 +137,7 @@ fn control_crawl(
result_receiver: mpsc::Receiver<CrawlResult>,
) -> Vec<Url> {
let mut crawl_state = CrawlState::new(&start_url);
let start_command = CrawlCommand {
url: start_url,
extract_links: true,
};
let start_command = CrawlCommand { url: start_url, extract_links: true };
command_sender.send(start_command).unwrap();
let mut pending_urls = 1;

View File

@ -2,9 +2,9 @@
Let us practice our new concurrency skills with
* Dining philosophers: a classic problem in concurrency.
- Dining philosophers: a classic problem in concurrency.
* Multi-threaded link checker: a larger project where you'll use Cargo to
- Multi-threaded link checker: a larger project where you'll use Cargo to
download dependencies and then check links in parallel.
<details>

View File

@ -23,4 +23,3 @@ _src/bin/client.rs_:
```rust,compile_fail
{{#include chat-async/src/bin/client.rs:solution}}
```