diff --git a/Cargo.lock b/Cargo.lock index e4848d43069e..8b4ba1c5478a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,57 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "async" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "reqwest", + "tokio", +] + +[[package]] +name = "async-control-flow" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "reqwest", + "tokio", +] + +[[package]] +name = "async-exercises" +version = "0.1.0" +dependencies = [ + "tokio", +] + +[[package]] +name = "async-pitfalls" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "reqwest", + "tokio", +] + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -322,6 +373,10 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "concurrency" +version = "0.1.0" + [[package]] name = "control-flow-basics" version = "0.1.0" @@ -703,11 +758,25 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", diff --git a/Cargo.toml b/Cargo.toml index 3a12574a013c..4cf316e641da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,9 @@ members = [ "src/borrowing", "src/control-flow-basics", "src/error-handling", + "src/concurrency", "src/concurrency/sync-exercises", + "src/concurrency/async-exercises", "src/concurrency/async-exercises/chat-async", "src/generics", "src/iterators", @@ -23,6 +25,9 @@ members = [ "src/std-traits", "src/std-types", "src/testing", + "src/concurrency/async", + "src/concurrency/async-control-flow", + "src/concurrency/async-pitfalls", "src/tuples-and-arrays", "src/types-and-values", "src/unsafe-rust", diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f563654c6295..43ba612fb23a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -140,7 +140,7 @@ - [Exercise: Builder Type](memory-management/exercise.md) - [Solution](memory-management/solution.md) - [Smart Pointers](smart-pointers.md) - - [`Box`](smart-pointers/box.md) + - [`Box`](smart-pointers/box.md) - [`Rc`](smart-pointers/rc.md) - [Owned Trait Objects](smart-pointers/trait-objects.md) - [Exercise: Binary Tree](smart-pointers/exercise.md) @@ -305,6 +305,7 @@ --- - [Welcome](bare-metal.md) +- [Setup](bare-metal/setup.md) - [`no_std`](bare-metal/no_std.md) - [A Minimal Example](bare-metal/minimal.md) - [`alloc`](bare-metal/alloc.md) @@ -356,6 +357,7 @@ --- - [Welcome](concurrency/welcome.md) +- [Fearless Concurrency](concurrency/fearless.md) - [Threads](concurrency/threads.md) - [Plain Threads](concurrency/threads/plain.md) - [Scoped Threads](concurrency/threads/scoped.md) @@ -385,7 +387,7 @@ - [Futures](concurrency/async/futures.md) - [Runtimes](concurrency/async/runtimes.md) - [Tokio](concurrency/async/runtimes/tokio.md) - - [Tasks](concurrency/async/tasks.md) + - [Channels and Control Flow](concurrency/async-control-flow.md) - [Async Channels](concurrency/async-control-flow/channels.md) - [Join](concurrency/async-control-flow/join.md) diff --git a/src/android.md b/src/android.md index 82bc767a0806..6716ff6991ba 100644 --- a/src/android.md +++ b/src/android.md @@ -5,9 +5,19 @@ session: Android # Welcome to Rust in Android -Rust is supported for system software on Android. This means that you can write -new services, libraries, drivers or even firmware in Rust (or improve existing -code as needed). +This is a one-day course about Rust in Android: you can write new Android +platform services, libraries, drivers or even firmware in Rust. + +## Target Audience + +This course builds on [Rust Fundamentals](welcome-day-1.md) and we expect you +are familiar with the basics of Rust. You should also be familiar with +development on the Android Platform (AOSP). + +## Class Format + +The class is meant to be very interactive! Please ask questions to drive the +exploration of Rust! > We will attempt to call Rust from one of your own projects today. So try to > find a little corner of your code base where we can move some lines of code to diff --git a/src/bare-metal.md b/src/bare-metal.md index a2e45527593e..0a1ec5feaea2 100644 --- a/src/bare-metal.md +++ b/src/bare-metal.md @@ -5,57 +5,23 @@ session: Morning # Welcome to Bare Metal Rust -This is a standalone one-day course about bare-metal Rust, aimed at people who -are familiar with the basics of Rust (perhaps from completing the Comprehensive -Rust course), and ideally also have some experience with bare-metal programming -in some other language such as C. +This is a one-day course about bare-metal Rust: running Rust code without an OS +underneath us. -Today we will talk about 'bare-metal' Rust: running Rust code without an OS -underneath us. This will be divided into several parts: +The class is divided into several parts: - What is `no_std` Rust? - Writing firmware for microcontrollers. - Writing bootloader / kernel code for application processors. - Some useful crates for bare-metal Rust development. -For the microcontroller part of the course we will use the -[BBC micro:bit](https://microbit.org/) v2 as an example. It's a -[development board](https://tech.microbit.org/hardware/) based on the Nordic -nRF52833 microcontroller with some LEDs and buttons, an I2C-connected -accelerometer and compass, and an on-board SWD debugger. +## Target Audience -To get started, install some tools we'll need later. On gLinux or Debian: +This course builds on [Rust Fundamentals](welcome-day-1.md) and we expect you +are familiar with the basics of Rust. You should ideally also have some +experience with bare-metal programming in some other language such as C. - +## Class Format -```bash -sudo apt install gcc-aarch64-linux-gnu gdb-multiarch libudev-dev picocom pkg-config qemu-system-arm -rustup update -rustup target add aarch64-unknown-none thumbv7em-none-eabihf -rustup component add llvm-tools-preview -cargo install cargo-binutils cargo-embed -``` - -And give users in the `plugdev` group access to the micro:bit programmer: - - - -```bash -echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", MODE="0664", GROUP="plugdev"' |\ - sudo tee /etc/udev/rules.d/50-microbit.rules -sudo udevadm control --reload-rules -``` - -On MacOS: - - - -```bash -xcode-select --install -brew install gdb picocom qemu -brew install --cask gcc-aarch64-embedded -rustup update -rustup target add aarch64-unknown-none thumbv7em-none-eabihf -rustup component add llvm-tools-preview -cargo install cargo-binutils cargo-embed -``` +The class is meant to be very interactive! Please ask questions to drive the +exploration of Rust! diff --git a/src/bare-metal/setup.md b/src/bare-metal/setup.md new file mode 100644 index 000000000000..04298029b8d5 --- /dev/null +++ b/src/bare-metal/setup.md @@ -0,0 +1,49 @@ +# Setup + +For the microcontroller part of the course we will use the +[BBC micro:bit](https://microbit.org/) v2 as an example. It's a +[development board](https://tech.microbit.org/hardware/) based on the Nordic +nRF52833 microcontroller with some LEDs and buttons, an I2C-connected +accelerometer and compass, and an on-board SWD debugger. + +To get started, install some tools we'll need later. + +## Linux + +Please run the following on gLinux or Debian: + + + +```bash +sudo apt install gcc-aarch64-linux-gnu gdb-multiarch libudev-dev picocom pkg-config qemu-system-arm +rustup update +rustup target add aarch64-unknown-none thumbv7em-none-eabihf +rustup component add llvm-tools-preview +cargo install cargo-binutils cargo-embed +``` + +And give users in the `plugdev` group access to the micro:bit programmer: + + + +```bash +echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", MODE="0664", GROUP="plugdev"' |\ + sudo tee /etc/udev/rules.d/50-microbit.rules +sudo udevadm control --reload-rules +``` + +## MacOS + +Please run the following on MacOS: + + + +```bash +xcode-select --install +brew install gdb picocom qemu +brew install --cask gcc-aarch64-embedded +rustup update +rustup target add aarch64-unknown-none thumbv7em-none-eabihf +rustup component add llvm-tools-preview +cargo install cargo-binutils cargo-embed +``` diff --git a/src/borrowing/interior-mutability.md b/src/borrowing/interior-mutability.md index def874e9c560..4fc543e78801 100644 --- a/src/borrowing/interior-mutability.md +++ b/src/borrowing/interior-mutability.md @@ -14,6 +14,8 @@ while still ensuring safety, typically by performing a runtime check. ## `RefCell` +A [`RefCell`] gives you mutable access to a value behind a shared reference: + ```rust,editable use std::cell::RefCell; @@ -36,9 +38,9 @@ fn main() { ## `Cell` -`Cell` wraps a value and allows getting or setting the value, even with a shared -reference to the `Cell`. However, it does not allow any references to the value. -Since there are no references, borrowing rules cannot be broken. +[`Cell`] wraps a `T` value. It allows getting or setting the value, even with +a shared reference to the `Cell`. However, it does not allow any references to +the value. Since there are no references, the borrowing rules cannot be broken. ```rust,editable use std::cell::Cell; @@ -72,3 +74,6 @@ that safety, and `RefCell` and `Cell` are two of them. have its own cost. + +[`Cell`]: https://doc.rust-lang.org/std/cell/struct.Cell.html +[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html diff --git a/src/chromium.md b/src/chromium.md index 442db003b900..7a79cb18c8e6 100644 --- a/src/chromium.md +++ b/src/chromium.md @@ -12,3 +12,14 @@ code to connect between Rust and existing Chromium C++ code. > a corner of the code where you're displaying a UTF8 string to the user, feel > free to follow this recipe in your part of the codebase instead of the exact > part we talk about. + +## Target Audience + +This course builds on [Rust Fundamentals](welcome-day-1.md) and we expect you +are familiar with the basics of Rust. You should also be familiar with Chromium +development. + +## Class Format + +The class is meant to be very interactive! Please ask questions to drive the +exploration of Rust! diff --git a/src/concurrency/async-control-flow/Cargo.toml b/src/concurrency/async-control-flow/Cargo.toml new file mode 100644 index 000000000000..a0b134130b6d --- /dev/null +++ b/src/concurrency/async-control-flow/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "async-control-flow" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "channels" +path = "channels.rs" + +[[bin]] +name = "join" +path = "join.rs" + +[[bin]] +name = "select" +path = "select.rs" + +[dependencies] +anyhow = "1.0.81" +async-trait = "0.1.79" +futures = { version = "0.3.30", default-features = false } +reqwest = { version = "0.12.1", default-features = false } +tokio = { version = "1.36.0", features = ["full"] } diff --git a/src/concurrency/async-control-flow/channels.md b/src/concurrency/async-control-flow/channels.md index 72678464be67..6bab6257e885 100644 --- a/src/concurrency/async-control-flow/channels.md +++ b/src/concurrency/async-control-flow/channels.md @@ -4,34 +4,10 @@ minutes: 8 # Async Channels -Several crates have support for asynchronous channels. For instance `tokio`: +Asynchronous channels are very similar to synchronous channels: ```rust,editable,compile_fail -use tokio::sync::mpsc::{self, Receiver}; - -async fn ping_handler(mut input: Receiver<()>) { - let mut count: usize = 0; - - while let Some(_) = input.recv().await { - count += 1; - println!("Received {count} pings so far."); - } - - println!("ping_handler complete"); -} - -#[tokio::main] -async fn main() { - let (sender, receiver) = mpsc::channel(32); - let ping_handler_task = tokio::spawn(ping_handler(receiver)); - for i in 0..10 { - sender.send(()).await.expect("Failed to send ping."); - println!("Sent {} pings so far.", i + 1); - } - - drop(sender); - ping_handler_task.await.expect("Something went wrong in ping handler task."); -} +{{#include channels.rs}} ```
diff --git a/src/concurrency/async-control-flow/channels.rs b/src/concurrency/async-control-flow/channels.rs new file mode 100644 index 000000000000..8163db9d239b --- /dev/null +++ b/src/concurrency/async-control-flow/channels.rs @@ -0,0 +1,23 @@ +use tokio::sync::mpsc; + +async fn ping_handler(mut input: mpsc::Receiver<()>) { + let mut count = 0; + while let Some(_) = input.recv().await { + count += 1; + println!("Received {count} pings so far."); + } + println!("ping_handler complete"); +} + +#[tokio::main] +async fn main() { + let (sender, receiver) = mpsc::channel(32); + let ping_handler_task = tokio::spawn(ping_handler(receiver)); + for i in 0..10 { + sender.send(()).await.expect("Failed to send ping."); + println!("Sent {} pings so far.", i + 1); + } + + drop(sender); + ping_handler_task.await.expect("Something went wrong in ping handler task."); +} diff --git a/src/concurrency/async-control-flow/join.md b/src/concurrency/async-control-flow/join.md index 6aa06e6d0d23..5962120aac87 100644 --- a/src/concurrency/async-control-flow/join.md +++ b/src/concurrency/async-control-flow/join.md @@ -9,30 +9,7 @@ collection of their results. This is similar to `Promise.all` in JavaScript or `asyncio.gather` in Python. ```rust,editable,compile_fail -use anyhow::Result; -use futures::future; -use reqwest; -use std::collections::HashMap; - -async fn size_of_page(url: &str) -> Result { - let resp = reqwest::get(url).await?; - Ok(resp.text().await?.len()) -} - -#[tokio::main] -async fn main() { - let urls: [&str; 4] = [ - "https://google.com", - "https://httpbin.org/ip", - "https://play.rust-lang.org/", - "BAD_URL", - ]; - let futures_iter = urls.into_iter().map(size_of_page); - let results = future::join_all(futures_iter).await; - let page_sizes_dict: HashMap<&str, Result> = - urls.into_iter().zip(results.into_iter()).collect(); - println!("{:?}", page_sizes_dict); -} +{{#include join.rs}} ```
diff --git a/src/concurrency/async-control-flow/join.rs b/src/concurrency/async-control-flow/join.rs new file mode 100644 index 000000000000..1a5c2f2793c1 --- /dev/null +++ b/src/concurrency/async-control-flow/join.rs @@ -0,0 +1,17 @@ +use futures::future::join_all; +use reqwest; + +async fn size_of_page(url: &str) -> reqwest::Result { + let resp = reqwest::get(url).await?; + Ok(resp.text().await?.len()) +} + +#[tokio::main] +async fn main() { + let urls = ["https://rust-lang.org", "https://httpbin.org/ip", "BAD_URL"]; + let futures = urls.into_iter().map(size_of_page); + let results = join_all(futures).await; + for (url, result) in urls.into_iter().zip(results) { + println!("{url}: {result:?}"); + } +} diff --git a/src/concurrency/async-control-flow/select.md b/src/concurrency/async-control-flow/select.md index b3df5c2f8539..257d37be572e 100644 --- a/src/concurrency/async-control-flow/select.md +++ b/src/concurrency/async-control-flow/select.md @@ -16,44 +16,7 @@ the resulting variables. The `statement` result becomes the result of the `select!` macro. ```rust,editable,compile_fail -use tokio::sync::mpsc::{self, Receiver}; -use tokio::time::{sleep, Duration}; - -#[derive(Debug, PartialEq)] -enum Animal { - Cat { name: String }, - Dog { name: String }, -} - -async fn first_animal_to_finish_race( - mut cat_rcv: Receiver, - mut dog_rcv: Receiver, -) -> Option { - tokio::select! { - cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }), - dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? }) - } -} - -#[tokio::main] -async fn main() { - let (cat_sender, cat_receiver) = mpsc::channel(32); - let (dog_sender, dog_receiver) = mpsc::channel(32); - tokio::spawn(async move { - sleep(Duration::from_millis(500)).await; - cat_sender.send(String::from("Felix")).await.expect("Failed to send cat."); - }); - tokio::spawn(async move { - sleep(Duration::from_millis(50)).await; - dog_sender.send(String::from("Rex")).await.expect("Failed to send dog."); - }); - - let winner = first_animal_to_finish_race(cat_receiver, dog_receiver) - .await - .expect("Failed to receive winner"); - - println!("Winner is {winner:?}"); -} +{{#include select.rs}} ```
diff --git a/src/concurrency/async-control-flow/select.rs b/src/concurrency/async-control-flow/select.rs new file mode 100644 index 000000000000..69f2e8d3fd03 --- /dev/null +++ b/src/concurrency/async-control-flow/select.rs @@ -0,0 +1,38 @@ +use tokio::sync::mpsc::{self, Receiver}; +use tokio::time::{sleep, Duration}; + +#[derive(Debug, PartialEq)] +enum Animal { + Cat { name: String }, + Dog { name: String }, +} + +async fn first_animal_to_finish_race( + mut cat_rcv: Receiver, + mut dog_rcv: Receiver, +) -> Option { + tokio::select! { + cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }), + dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? }) + } +} + +#[tokio::main] +async fn main() { + let (cat_sender, cat_receiver) = mpsc::channel(32); + let (dog_sender, dog_receiver) = mpsc::channel(32); + tokio::spawn(async move { + sleep(Duration::from_millis(500)).await; + cat_sender.send(String::from("Felix")).await.expect("Failed to send cat."); + }); + tokio::spawn(async move { + sleep(Duration::from_millis(50)).await; + dog_sender.send(String::from("Rex")).await.expect("Failed to send dog."); + }); + + let winner = first_animal_to_finish_race(cat_receiver, dog_receiver) + .await + .expect("Failed to receive winner"); + + println!("Winner is {winner:?}"); +} diff --git a/src/concurrency/async-exercises/Cargo.toml b/src/concurrency/async-exercises/Cargo.toml new file mode 100644 index 000000000000..8bd284a32edb --- /dev/null +++ b/src/concurrency/async-exercises/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "async-exercises" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "dining-philosophers-async" +path = "dining-philosophers.rs" + +[dependencies] +tokio = { version = "1.36.0", features = ["full"] } diff --git a/src/concurrency/async-exercises/chat-app.md b/src/concurrency/async-exercises/chat-app.md index 1fc9bddbaf0e..416bc6e993a0 100644 --- a/src/concurrency/async-exercises/chat-app.md +++ b/src/concurrency/async-exercises/chat-app.md @@ -29,13 +29,13 @@ You are going to need the following functions from `tokio` and [`tokio_websockets`][2]. Spend a few minutes to familiarize yourself with the API. -- [StreamExt::next()][3] implemented by `WebSocketStream`: for asynchronously +- [`StreamExt::next()`][3] implemented by `WebSocketStream`: for asynchronously reading messages from a Websocket Stream. -- [SinkExt::send()][4] implemented by `WebSocketStream`: for asynchronously +- [`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 +- [`Lines::next_line()`][5]: for asynchronously reading user messages from the standard input. -- [Sender::subscribe()][6]: for subscribing to a broadcast channel. +- [`Sender::subscribe()`][6]: for subscribing to a broadcast channel. ## Two binaries @@ -58,9 +58,8 @@ _src/bin/server.rs_: {{#include chat-async/src/bin/server.rs:setup}} {{#include chat-async/src/bin/server.rs:handle_connection}} - // TODO: For a hint, see the description of the task below. - + Ok(()) {{#include chat-async/src/bin/server.rs:main}} ``` @@ -72,7 +71,7 @@ _src/bin/client.rs_: {{#include chat-async/src/bin/client.rs:setup}} // TODO: For a hint, see the description of the task below. - + Ok(()) } ``` diff --git a/src/concurrency/async-exercises/dining-philosophers.md b/src/concurrency/async-exercises/dining-philosophers.md index a1d783cc5ca4..5c3bfff48695 100644 --- a/src/concurrency/async-exercises/dining-philosophers.md +++ b/src/concurrency/async-exercises/dining-philosophers.md @@ -36,7 +36,7 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that } ``` -Since this time you are using Async Rust, you'll need a `tokio` dependency. You +Since this time you are using async Rust, you'll need a `tokio` dependency. You can use the following `Cargo.toml`: diff --git a/src/concurrency/async-exercises/dining-philosophers.rs b/src/concurrency/async-exercises/dining-philosophers.rs index 12cb08a96c9e..537bbec73f11 100644 --- a/src/concurrency/async-exercises/dining-philosophers.rs +++ b/src/concurrency/async-exercises/dining-philosophers.rs @@ -45,23 +45,10 @@ impl Philosopher { // ANCHOR_END: Philosopher-eat let (_left_fork, _right_fork) = loop { // Pick up forks... - let left_fork = self.left_fork.try_lock(); - let right_fork = self.right_fork.try_lock(); - let Ok(left_fork) = left_fork else { - // If we didn't get the left fork, drop the right fork if we - // have it and let other tasks make progress. - drop(right_fork); - time::sleep(time::Duration::from_millis(1)).await; - continue; + match (self.left_fork.try_lock(), self.right_fork.try_lock()) { + (Ok(left_fork), Ok(right_fork)) => break (left_fork, right_fork), + (_, _) => continue, }; - let Ok(right_fork) = right_fork else { - // If we didn't get the right fork, drop the left fork and let - // other tasks make progress. - drop(left_fork); - time::sleep(time::Duration::from_millis(1)).await; - continue; - }; - break (left_fork, right_fork); }; // ANCHOR: Philosopher-eat-body diff --git a/src/concurrency/async-pitfalls/Cargo.toml b/src/concurrency/async-pitfalls/Cargo.toml new file mode 100644 index 000000000000..a14bb6d504fb --- /dev/null +++ b/src/concurrency/async-pitfalls/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "async-pitfalls" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "async-traits" +path = "async-traits.rs" + +[[bin]] +name = "cancellation" +path = "cancellation.rs" + +[[bin]] +name = "pin" +path = "pin.rs" + +[dependencies] +anyhow = "1.0.81" +async-trait = "0.1.79" +futures = { version = "0.3.30", default-features = false } +reqwest = { version = "0.12.1", default-features = false } +tokio = { version = "1.36.0", features = ["full"] } diff --git a/src/concurrency/async-pitfalls/async-traits.md b/src/concurrency/async-pitfalls/async-traits.md index 5b285ec1780b..0d7cf7ddad37 100644 --- a/src/concurrency/async-pitfalls/async-traits.md +++ b/src/concurrency/async-pitfalls/async-traits.md @@ -4,66 +4,23 @@ minutes: 5 # Async Traits -Async methods in traits are were stabilized only recently, in the 1.75 release. -This required support for using return-position `impl Trait` (RPIT) in traits, -as the desugaring for `async fn` includes `-> impl Future`. +Async methods in traits are were stabilized in the 1.75 release (December 2023). +This required support for using return-position `impl Trait` in traits, as the +desugaring for `async fn` includes `-> impl Future`. -However, even with the native support today there are some pitfalls around -`async fn` and RPIT in traits: +However, even with the native support, there are some pitfalls around +`async fn`: -- Return-position impl Trait captures all in-scope lifetimes (so some patterns - of borrowing cannot be expressed) +- Return-position `impl Trait` captures all in-scope lifetimes (so some patterns + of borrowing cannot be expressed). -- Traits whose methods use return-position `impl trait` or `async` are not `dyn` - compatible. +- Async traits cannot be used with [trait objects] (`dyn Trait` support). -If we do need `dyn` support, the crate -[async_trait](https://docs.rs/async-trait/latest/async_trait/) provides a -workaround through a macro, with some caveats: +The [async_trait] crate provides a workaround for `dyn` support through a macro, +with some caveats: ```rust,editable,compile_fail -use async_trait::async_trait; -use std::time::Instant; -use tokio::time::{sleep, Duration}; - -#[async_trait] -trait Sleeper { - async fn sleep(&self); -} - -struct FixedSleeper { - sleep_ms: u64, -} - -#[async_trait] -impl Sleeper for FixedSleeper { - async fn sleep(&self) { - sleep(Duration::from_millis(self.sleep_ms)).await; - } -} - -async fn run_all_sleepers_multiple_times( - sleepers: Vec>, - n_times: usize, -) { - for _ in 0..n_times { - println!("running all sleepers.."); - for sleeper in &sleepers { - let start = Instant::now(); - sleeper.sleep().await; - println!("slept for {}ms", start.elapsed().as_millis()); - } - } -} - -#[tokio::main] -async fn main() { - let sleepers: Vec> = vec![ - Box::new(FixedSleeper { sleep_ms: 50 }), - Box::new(FixedSleeper { sleep_ms: 100 }), - ]; - run_all_sleepers_multiple_times(sleepers, 5).await; -} +{{#include async-traits.rs}} ```
@@ -71,13 +28,21 @@ async fn main() { - `async_trait` is easy to use, but note that it's using heap allocations to achieve this. This heap allocation has performance overhead. -- The challenges in language support for `async trait` are deep Rust and - probably not worth describing in-depth. Niko Matsakis did a good job of - explaining them in - [this post](https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/) - if you are interested in digging deeper. +- The challenges in language support for `async trait` are too deep to describe + in-depth in this class. See [this blog post] by Niko Matsakis if you are + interested in digging deeper. See also these keywords: + + - [RPIT]: short for + [return-position `impl Trait`](../../generics/impl-trait.md). + - [RPITIT]: short for return-position `impl Trait` in trait (RPIT in trait). - Try creating a new sleeper struct that will sleep for a random amount of time - and adding it to the Vec. + and adding it to the `Vec`.
+ +[async_trait]: https://docs.rs/async-trait/ +[trait objects]: ../../smart-pointers/trait-objects.md +[this blog post]: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/ +[RPIT]: https://doc.rust-lang.org/reference/types/impl-trait.html#abstract-return-types +[RPITIT]: https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html diff --git a/src/concurrency/async-pitfalls/async-traits.rs b/src/concurrency/async-pitfalls/async-traits.rs new file mode 100644 index 000000000000..be3abcce69b2 --- /dev/null +++ b/src/concurrency/async-pitfalls/async-traits.rs @@ -0,0 +1,38 @@ +use async_trait::async_trait; +use tokio::time::{sleep, Duration}; + +#[async_trait] +trait Sleeper { + async fn sleep(&self); +} + +struct FixedSleeper { + sleep_ms: u64, +} + +#[async_trait] +impl Sleeper for FixedSleeper { + async fn sleep(&self) { + sleep(Duration::from_millis(self.sleep_ms)).await; + } +} + +async fn run_all_sleepers_multiple_times(sleepers: Vec>) { + for _ in 0..5 { + println!("Running all sleepers..."); + for sleeper in &sleepers { + let start = std::time::Instant::now(); + sleeper.sleep().await; + println!("Slept for {} ms", start.elapsed().as_millis()); + } + } +} + +#[tokio::main] +async fn main() { + let sleepers: Vec> = vec![ + Box::new(FixedSleeper { sleep_ms: 50 }), + Box::new(FixedSleeper { sleep_ms: 100 }), + ]; + run_all_sleepers_multiple_times(sleepers).await; +} diff --git a/src/concurrency/async-pitfalls/cancellation.md b/src/concurrency/async-pitfalls/cancellation.md index 66fae4e6e429..adb258ab17da 100644 --- a/src/concurrency/async-pitfalls/cancellation.md +++ b/src/concurrency/async-pitfalls/cancellation.md @@ -10,65 +10,7 @@ the system works correctly even when futures are cancelled. For example, it shouldn't deadlock or lose data. ```rust,editable,compile_fail -use std::io::{self, ErrorKind}; -use std::time::Duration; -use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream}; - -struct LinesReader { - stream: DuplexStream, -} - -impl LinesReader { - fn new(stream: DuplexStream) -> Self { - Self { stream } - } - - async fn next(&mut self) -> io::Result> { - let mut bytes = Vec::new(); - let mut buf = [0]; - while self.stream.read(&mut buf[..]).await? != 0 { - bytes.push(buf[0]); - if buf[0] == b'\n' { - break; - } - } - if bytes.is_empty() { - return Ok(None); - } - let s = String::from_utf8(bytes) - .map_err(|_| io::Error::new(ErrorKind::InvalidData, "not UTF-8"))?; - Ok(Some(s)) - } -} - -async fn slow_copy(source: String, mut dest: DuplexStream) -> std::io::Result<()> { - for b in source.bytes() { - dest.write_u8(b).await?; - tokio::time::sleep(Duration::from_millis(10)).await - } - Ok(()) -} - -#[tokio::main] -async fn main() -> std::io::Result<()> { - let (client, server) = tokio::io::duplex(5); - let handle = tokio::spawn(slow_copy("hi\nthere\n".to_owned(), client)); - - let mut lines = LinesReader::new(server); - let mut interval = tokio::time::interval(Duration::from_millis(60)); - loop { - tokio::select! { - _ = interval.tick() => println!("tick!"), - line = lines.next() => if let Some(l) = line? { - print!("{}", l) - } else { - break - }, - } - } - handle.await.unwrap()?; - Ok(()) -} +{{#include cancellation.rs}} ```
diff --git a/src/concurrency/async-pitfalls/cancellation.rs b/src/concurrency/async-pitfalls/cancellation.rs new file mode 100644 index 000000000000..d771e6ee2cdf --- /dev/null +++ b/src/concurrency/async-pitfalls/cancellation.rs @@ -0,0 +1,59 @@ +use std::io::{self, ErrorKind}; +use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream}; + +struct LinesReader { + stream: DuplexStream, +} + +impl LinesReader { + fn new(stream: DuplexStream) -> Self { + Self { stream } + } + + async fn next(&mut self) -> io::Result> { + let mut bytes = Vec::new(); + let mut buf = [0]; + while self.stream.read(&mut buf[..]).await? != 0 { + bytes.push(buf[0]); + if buf[0] == b'\n' { + break; + } + } + if bytes.is_empty() { + return Ok(None); + } + let s = String::from_utf8(bytes) + .map_err(|_| io::Error::new(ErrorKind::InvalidData, "not UTF-8"))?; + Ok(Some(s)) + } +} + +async fn slow_copy(source: String, mut dest: DuplexStream) -> std::io::Result<()> { + for b in source.bytes() { + dest.write_u8(b).await?; + tokio::time::sleep(Duration::from_millis(10)).await + } + Ok(()) +} + +#[tokio::main] +async fn main() -> std::io::Result<()> { + let (client, server) = tokio::io::duplex(5); + let handle = tokio::spawn(slow_copy("hi\nthere\n".to_owned(), client)); + + let mut lines = LinesReader::new(server); + let mut interval = tokio::time::interval(Duration::from_millis(60)); + loop { + tokio::select! { + _ = interval.tick() => println!("tick!"), + line = lines.next() => if let Some(l) = line? { + print!("{}", l) + } else { + break + }, + } + } + handle.await.unwrap()?; + Ok(()) +} diff --git a/src/concurrency/async-pitfalls/pin.md b/src/concurrency/async-pitfalls/pin.md index fc764a8af083..84bf8795e44a 100644 --- a/src/concurrency/async-pitfalls/pin.md +++ b/src/concurrency/async-pitfalls/pin.md @@ -5,9 +5,11 @@ minutes: 20 # `Pin` Async blocks and functions return types implementing the `Future` trait. The -type returned is the result of a compiler transformation which turns local +type returned is the result of a [compiler transformation] which turns local variables into data stored inside the future. +[compiler transformation]: https://tokio.rs/tokio/tutorial/async + Some of those variables can hold pointers to other local variables. Because of that, the future should never be moved to a different memory location, as it would invalidate those pointers. @@ -18,54 +20,7 @@ operations that would move the instance it points to into a different memory location. ```rust,editable,compile_fail -use tokio::sync::{mpsc, oneshot}; -use tokio::task::spawn; -use tokio::time::{sleep, Duration}; - -// A work item. In this case, just sleep for the given time and respond -// with a message on the `respond_on` channel. -#[derive(Debug)] -struct Work { - input: u32, - respond_on: oneshot::Sender, -} - -// A worker which listens for work on a queue and performs it. -async fn worker(mut work_queue: mpsc::Receiver) { - let mut iterations = 0; - loop { - tokio::select! { - Some(work) = work_queue.recv() => { - sleep(Duration::from_millis(10)).await; // Pretend to work. - work.respond_on - .send(work.input * 1000) - .expect("failed to send response"); - iterations += 1; - } - // TODO: report number of iterations every 100ms - } - } -} - -// A requester which requests work and waits for it to complete. -async fn do_work(work_queue: &mpsc::Sender, input: u32) -> u32 { - let (tx, rx) = oneshot::channel(); - work_queue - .send(Work { input, respond_on: tx }) - .await - .expect("failed to send on work queue"); - rx.await.expect("failed waiting for response") -} - -#[tokio::main] -async fn main() { - let (tx, rx) = mpsc::channel(10); - spawn(worker(rx)); - for i in 0..100 { - let resp = do_work(&tx, i).await; - println!("work result for iteration {i}: {resp}"); - } -} +{{#include pin.rs}} ```
diff --git a/src/concurrency/async-pitfalls/pin.rs b/src/concurrency/async-pitfalls/pin.rs new file mode 100644 index 000000000000..6e03174a4ec3 --- /dev/null +++ b/src/concurrency/async-pitfalls/pin.rs @@ -0,0 +1,40 @@ +use anyhow::Result; +use tokio::sync::{mpsc, oneshot}; +use tokio::time::{sleep, Duration}; + +#[derive(Debug)] +struct Work { + input: u32, + respond_on: oneshot::Sender, +} + +async fn worker(mut work_queue: mpsc::Receiver) { + let mut _iterations = 0; + loop { + tokio::select! { + Some(work) = work_queue.recv() => { + sleep(Duration::from_millis(10)).await; // Pretend to work. + work.respond_on.send(work.input * 1000).unwrap(); + _iterations += 1; + } + // TODO: report number of iterations every 100ms + } + } +} + +async fn do_work(work_queue: &mpsc::Sender, input: u32) -> Result { + let (tx, rx) = oneshot::channel(); + work_queue.send(Work { input, respond_on: tx }).await?; + Ok(rx.await?) +} + +#[tokio::main] +async fn main() -> Result<()> { + let (tx, rx) = mpsc::channel(10); + tokio::spawn(worker(rx)); + for i in 0..100 { + let resp = do_work(&tx, i).await?; + println!("work result for iteration {i}: {resp}"); + } + Ok(()) +} diff --git a/src/concurrency/async/Cargo.toml b/src/concurrency/async/Cargo.toml new file mode 100644 index 000000000000..f60f74e2bd84 --- /dev/null +++ b/src/concurrency/async/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "async" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "tasks" +path = "tasks.rs" + +[dependencies] +anyhow = "1.0.81" +async-trait = "0.1.79" +futures = { version = "0.3.30", default-features = false } +reqwest = { version = "0.12.1", default-features = false } +tokio = { version = "1.36.0", features = ["full"] } diff --git a/src/concurrency/async/tasks.md b/src/concurrency/async/tasks.md index 2c23a0064c14..2941cd4a7f91 100644 --- a/src/concurrency/async/tasks.md +++ b/src/concurrency/async/tasks.md @@ -12,30 +12,7 @@ corresponding loosely to a call stack. Concurrency within a task is possible by polling multiple child futures, such as racing a timer and an I/O operation. ```rust,compile_fail -use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; -use tokio::net::TcpListener; - -#[tokio::main] -async fn main() -> io::Result<()> { - let listener = TcpListener::bind("127.0.0.1:0").await?; - println!("listening on port {}", listener.local_addr()?.port()); - - loop { - let (mut socket, addr) = listener.accept().await?; - - println!("connection from {addr:?}"); - - tokio::spawn(async move { - socket.write_all(b"Who are you?\n").await.expect("socket error"); - - let mut buf = vec![0; 1024]; - let name_size = socket.read(&mut buf).await.expect("socket error"); - let name = std::str::from_utf8(&buf[..name_size]).unwrap().trim(); - let reply = format!("Thanks for dialing in, {name}!\n"); - socket.write_all(reply.as_bytes()).await.expect("socket error"); - }); - } -} +{{#include tasks.rs}} ```
diff --git a/src/concurrency/async/tasks.rs b/src/concurrency/async/tasks.rs new file mode 100644 index 000000000000..14b1f8637521 --- /dev/null +++ b/src/concurrency/async/tasks.rs @@ -0,0 +1,24 @@ +use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpListener; + +#[tokio::main] +async fn main() -> io::Result<()> { + let listener = TcpListener::bind("127.0.0.1:0").await?; + println!("listening on port {}", listener.local_addr()?.port()); + + loop { + let (mut socket, addr) = listener.accept().await?; + + println!("connection from {addr:?}"); + + tokio::spawn(async move { + socket.write_all(b"Who are you?\n").await.expect("socket error"); + + let mut buf = vec![0; 1024]; + let name_size = socket.read(&mut buf).await.expect("socket error"); + let name = std::str::from_utf8(&buf[..name_size]).unwrap().trim(); + let reply = format!("Thanks for dialing in, {name}!\n"); + socket.write_all(reply.as_bytes()).await.expect("socket error"); + }); + } +} diff --git a/src/concurrency/channels/bounded.md b/src/concurrency/channels/bounded.md index e1667d4f54ec..b1500b34c0e4 100644 --- a/src/concurrency/channels/bounded.md +++ b/src/concurrency/channels/bounded.md @@ -4,7 +4,7 @@ minutes: 8 # Bounded Channels -With bounded (synchronous) channels, `send` can block the current thread: +With bounded (synchronous) channels, [`send()`] can block the current thread: ```rust,editable use std::sync::mpsc; @@ -32,12 +32,15 @@ fn main() {
-- Calling `send` will block the current thread until there is space in the +- Calling `send()` will block the current thread until there is space in the channel for the new message. The thread can be blocked indefinitely if there is nobody who reads from the channel. -- A call to `send` will abort with an error (that is why it returns `Result`) if - the channel is closed. A channel is closed when the receiver is dropped. +- A call to `send()` will abort with an error (that is why it returns `Result`) + if the channel is closed. A channel is closed when the receiver is dropped. - A bounded channel with a size of zero is called a "rendezvous channel". Every - send will block the current thread until another thread calls `recv`. + send will block the current thread until another thread calls [`recv()`].
+ +[`send()`]: https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html#method.send +[`recv()`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html#method.recv diff --git a/src/concurrency/channels/senders-receivers.md b/src/concurrency/channels/senders-receivers.md index c0ece3576a65..2609196efd73 100644 --- a/src/concurrency/channels/senders-receivers.md +++ b/src/concurrency/channels/senders-receivers.md @@ -4,8 +4,8 @@ minutes: 9 # Senders and Receivers -Rust channels have two parts: a `Sender` and a `Receiver`. The two parts -are connected via the channel, but you only see the end-points. +Rust channels have two parts: a [`Sender`] and a [`Receiver`]. The two +parts are connected via the channel, but you only see the end-points. ```rust,editable use std::sync::mpsc; @@ -27,10 +27,16 @@ fn main() {
-- `mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender` +- [`mpsc`] stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender` implement `Clone` (so you can make multiple producers) but `Receiver` does not. -- `send()` and `recv()` return `Result`. If they return `Err`, it means the +- [`send()`] and [`recv()`] return `Result`. If they return `Err`, it means the counterpart `Sender` or `Receiver` is dropped and the channel is closed.
+ +[`Sender`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html +[`Receiver`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html +[`send()`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html#method.send +[`recv()`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html#method.recv +[`mpsc`]: https://doc.rust-lang.org/std/sync/mpsc/index.html diff --git a/src/concurrency/channels/unbounded.md b/src/concurrency/channels/unbounded.md index c06641218ea1..c02d5a8f55a1 100644 --- a/src/concurrency/channels/unbounded.md +++ b/src/concurrency/channels/unbounded.md @@ -4,7 +4,7 @@ minutes: 2 # Unbounded Channels -You get an unbounded and asynchronous channel with `mpsc::channel()`: +You get an unbounded and asynchronous channel with [`mpsc::channel()`]: ```rust,editable use std::sync::mpsc; @@ -29,3 +29,18 @@ fn main() { } } ``` + +
+ +- The channel is called asynchronous because there is no synchronization between + sending and receiving. +- The channel buffers the values. The buffer grows automatically, similar to how + a `Vec` grows when you push data to it. +- The channel takes ownership of the values when you call [`send()`]. This is + seen in the signature: it takes `T` by value. You thus lose access to the + value you send into a channel. + +
+ +[`mpsc::channel()`]: https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html +[`send()`]: https://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html#method.send diff --git a/src/concurrency/fearless.md b/src/concurrency/fearless.md new file mode 100644 index 000000000000..7ead3cc52f1b --- /dev/null +++ b/src/concurrency/fearless.md @@ -0,0 +1,15 @@ +# Fearless Concurrency + +Rust has great support for concurrency: + +- The type system is able to prevent many concurrency bugs at compile time. +- This is often referred to as _fearless concurrency_. You can refactor without + fear of introducing concurrency issues. + +
+ +- Rust lets us access OS concurrency primitives such as threads and mutexes. +- We will see how the type system gives prevents certain kinds of concurrency + bugs when using multiple threads. + +
diff --git a/src/concurrency/shared-state/arc.md b/src/concurrency/shared-state/arc.md index 694a6dc29d4c..424f846ff77c 100644 --- a/src/concurrency/shared-state/arc.md +++ b/src/concurrency/shared-state/arc.md @@ -4,7 +4,8 @@ minutes: 5 # `Arc` -[`Arc`][1] allows shared read-only access via `Arc::clone`: +[`Arc`] is a reference-counted shared pointer to `T`. Use this when you need +to refer to the same data from multiple threads: ```rust,editable use std::sync::Arc; @@ -26,18 +27,21 @@ fn main() { } ``` -[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html -
-- `Arc` stands for "Atomic Reference Counted", a thread safe version of `Rc` - that uses atomic operations. +- `Arc` is short for "Atomically Reference Counted". It uses atomic + (thread-safe) CPU instructions to maintain the reference count. `Arc` is a + thread safe version of [`Rc`]. - `Arc` implements `Clone` whether or not `T` does. It implements `Send` and `Sync` if and only if `T` implements them both. - `Arc::clone()` has the cost of atomic operations that get executed, but after that the use of the `T` is free. - Beware of reference cycles, `Arc` does not use a garbage collector to detect them. - - `std::sync::Weak` can help. + - [`std::sync::Weak`] can help avoid reference cycles.
+ +[`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html +[`Rc`]: ../../smart-pointers/rc.md +[`std::sync::Weak`]: https://doc.rust-lang.org/std/sync/struct.Weak.html diff --git a/src/concurrency/shared-state/mutex.md b/src/concurrency/shared-state/mutex.md index 555ee55f8a97..6cefc9da10d0 100644 --- a/src/concurrency/shared-state/mutex.md +++ b/src/concurrency/shared-state/mutex.md @@ -4,9 +4,9 @@ minutes: 14 # `Mutex` -[`Mutex`][1] ensures mutual exclusion _and_ allows mutable access to `T` -behind a read-only interface (another form of -[interior mutability](../../borrowing/interior-mutability)): +[`Mutex`] ensures mutual exclusion _and_ allows mutable access to `T` behind +a read-only interface (another form of +[interior mutability](../../borrowing/interior-mutability.md)): ```rust,editable use std::sync::Mutex; @@ -27,9 +27,7 @@ fn main() { Notice how we have a [`impl Sync for Mutex`][2] blanket implementation. -[1]: https://doc.rust-lang.org/std/sync/struct.Mutex.html [2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html#impl-Sync-for-Mutex%3CT%3E -[3]: https://doc.rust-lang.org/std/sync/struct.Arc.html
@@ -41,13 +39,15 @@ implementation. `MutexGuard` ensures that the `&mut T` doesn't outlive the lock being held. - `Mutex` implements both `Send` and `Sync` iff (if and only if) `T` implements `Send`. -- A read-write lock counterpart: `RwLock`. +- Rust has a multi-reader single-writer lock counterpart: [`RwLock`]. - Why does `lock()` return a `Result`? - If the thread that held the `Mutex` panicked, the `Mutex` becomes "poisoned" to signal that the data it protected might be in an inconsistent state. Calling `lock()` on a poisoned mutex fails with a [`PoisonError`]. You can call `into_inner()` on the error to recover the data regardless. -[`PoisonError`]: https://doc.rust-lang.org/std/sync/struct.PoisonError.html -
+ +[`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html +[`RwLock`]: https://doc.rust-lang.org/std/sync/struct.RwLock.html +[`PoisonError`]: https://doc.rust-lang.org/std/sync/struct.PoisonError.html diff --git a/src/concurrency/welcome.md b/src/concurrency/welcome.md index 0a35006d02dd..5edb39ae74b7 100644 --- a/src/concurrency/welcome.md +++ b/src/concurrency/welcome.md @@ -6,23 +6,34 @@ target_minutes: 180 # Welcome to Concurrency in Rust -Rust has full support for concurrency using OS threads with mutexes and -channels. +This is one-day course about concurrency in Rust: structuring your program so it +does multiple things concurrently or in parallel. -The Rust type system plays an important role in making many concurrency bugs -compile time bugs. This is often referred to as _fearless concurrency_ since you -can rely on the compiler to ensure correctness at runtime. +We will cover two major parts of Rust today: + +- Multi-threaded programming using threads and mutexes. +- Concurrent programming using the `async` keyword. ## Schedule {{%session outline}} -
+## Target Audience + +This course builds on [Rust Fundamentals](../welcome-day-1.md). To get the most +out of the class, we expect that you are familiar with the basics of Rust, as +well as concepts such as: + +- [Borrowing](../borrowing.md): you should understand the difference between + shared borrows (`&T`) and exclusive borrows (`&mut T`), +- [Generics](../generics.md): we will use a lot of + [trait bounds](../generics/trait-bounds.md). +- [Closures](../std-traits/closures.md): make sure you understand how closures + capture values from their environment. +- [`Rc`](../smart-pointers/rc.md): we will use a similar type for shared + ownership. -- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc. -- The type system gives us safety for concurrency without any special features. -- The same tools that help with "concurrent" access in a single thread (e.g., a - called function that might mutate an argument or save references to it to read - later) save us from multi-threading issues. +## Class Format -
+The class is meant to be very interactive! Please ask questions to drive the +exploration of Rust! diff --git a/src/smart-pointers/box.md b/src/smart-pointers/box.md index dbf91cd0e328..bd6b247677d4 100644 --- a/src/smart-pointers/box.md +++ b/src/smart-pointers/box.md @@ -2,10 +2,10 @@ minutes: 8 --- -# `Box` +# `Box` -[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned pointer -to data on the heap: +[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned +pointer to a `T` on the heap: ```rust,editable fn main() { @@ -28,8 +28,7 @@ fn main() { ``` `Box` implements `Deref`, which means that you can -[call methods -from `T` directly on a `Box`](https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion). +[call methods from `T` directly](https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion). Recursive data types or data types with dynamic sizes need to use a `Box`: diff --git a/src/smart-pointers/rc.md b/src/smart-pointers/rc.md index 6b55d5f4fd31..804f6fb5fea3 100644 --- a/src/smart-pointers/rc.md +++ b/src/smart-pointers/rc.md @@ -4,8 +4,8 @@ minutes: 5 # `Rc` -[`Rc`][1] is a reference-counted shared pointer. Use this when you need to refer -to the same data from multiple places: +[`Rc`][1] is a reference-counted shared pointer to `T`. Use this when you +need to refer to the same data from multiple places: ```rust,editable use std::rc::Rc; @@ -24,8 +24,8 @@ fn main() { cycles that will get dropped. [1]: https://doc.rust-lang.org/std/rc/struct.Rc.html -[2]: ../concurrency/shared_state/arc.md -[3]: https://doc.rust-lang.org/std/sync/struct.Mutex.html +[2]: ../concurrency/shared-state/arc.md +[3]: ../concurrency/shared-state/mutex.md [4]: https://doc.rust-lang.org/std/rc/struct.Weak.html