diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa3210b0..ba453b0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,20 +9,16 @@ on: jobs: test: name: build and test - env: - MDBOOK_LINKCHECK_VERSION: 0.7.0 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update stable && rustup default stable - run: sudo apt-get update && sudo apt-get install aspell aspell-en - - name: Install mdbook and mdbook-linkcheck - run: | - tag=$(curl -LsSf https://api.github.com/repos/rust-lang/mdBook/releases/latest | jq -r '.tag_name') - curl -LsSf https://github.com/rust-lang/mdBook/releases/download/$tag/mdbook-$tag-x86_64-unknown-linux-gnu.tar.gz | tar xzf - - curl -LsSf https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/download/v${MDBOOK_LINKCHECK_VERSION}/mdbook-linkcheck-v${MDBOOK_LINKCHECK_VERSION}-x86_64-unknown-linux-gnu.tar.gz | tar xzf - - echo $(pwd) >> $GITHUB_PATH + - name: Install mdbook + uses: taiki-e/install-action@mdbook + - name: Install mdbook-linkcheck + uses: taiki-e/install-action@mdbook-linkcheck - run: bash ci/spellcheck.sh list - run: mdbook build - run: cargo test --all --manifest-path=./examples/Cargo.toml --target-dir ./target diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..6553e5cb --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,2 @@ +# https://github.com/rust-lang/async-book/pull/59#issuecomment-556240879 +disable_all_formatting = true diff --git a/ci/dictionary.txt b/ci/dictionary.txt index aeab728f..5c0fe901 100644 --- a/ci/dictionary.txt +++ b/ci/dictionary.txt @@ -9,6 +9,7 @@ AsyncRead AsyncWrite AwaitingFutOne AwaitingFutTwo +cancelling combinator combinators compat @@ -37,6 +38,7 @@ interprocess IoBlocker IOCP IoObject +JoinHandle kqueue localhost LocalExecutor diff --git a/examples/01_02_why_async/Cargo.toml b/examples/01_02_why_async/Cargo.toml index 76519f21..8a75947f 100644 --- a/examples/01_02_why_async/Cargo.toml +++ b/examples/01_02_why_async/Cargo.toml @@ -2,7 +2,7 @@ name = "example_01_02_why_async" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/01_02_why_async/src/lib.rs b/examples/01_02_why_async/src/lib.rs index 64758e4f..f2ecba96 100644 --- a/examples/01_02_why_async/src/lib.rs +++ b/examples/01_02_why_async/src/lib.rs @@ -1,12 +1,7 @@ #![cfg(test)] -use { - futures::{ - executor::block_on, - join, - }, - std::thread, -}; +use futures::{executor::block_on, join}; +use std::thread; fn download(_url: &str) { // ... diff --git a/examples/01_04_async_await_primer/Cargo.toml b/examples/01_04_async_await_primer/Cargo.toml index 74644d11..5e81d70e 100644 --- a/examples/01_04_async_await_primer/Cargo.toml +++ b/examples/01_04_async_await_primer/Cargo.toml @@ -2,7 +2,7 @@ name = "example_01_04_async_await_primer" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/02_02_future_trait/Cargo.toml b/examples/02_02_future_trait/Cargo.toml index fd40d625..4c5f9444 100644 --- a/examples/02_02_future_trait/Cargo.toml +++ b/examples/02_02_future_trait/Cargo.toml @@ -2,6 +2,6 @@ name = "example_02_02_future_trait" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/02_03_timer/Cargo.toml b/examples/02_03_timer/Cargo.toml index 937ba2d4..75152e7d 100644 --- a/examples/02_03_timer/Cargo.toml +++ b/examples/02_03_timer/Cargo.toml @@ -2,7 +2,7 @@ name = "example_02_03_timer" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/02_04_executor/Cargo.toml b/examples/02_04_executor/Cargo.toml index 9f94f067..5da97944 100644 --- a/examples/02_04_executor/Cargo.toml +++ b/examples/02_04_executor/Cargo.toml @@ -2,7 +2,7 @@ name = "example_02_04_executor" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/02_04_executor/src/lib.rs b/examples/02_04_executor/src/lib.rs index 7ab229ee..c57deae9 100644 --- a/examples/02_04_executor/src/lib.rs +++ b/examples/02_04_executor/src/lib.rs @@ -1,21 +1,19 @@ #![cfg(test)] // ANCHOR: imports -use { - futures::{ - future::{BoxFuture, FutureExt}, - task::{waker_ref, ArcWake}, - }, - std::{ - future::Future, - sync::mpsc::{sync_channel, Receiver, SyncSender}, - sync::{Arc, Mutex}, - task::Context, - time::Duration, - }, - // The timer we wrote in the previous section: - timer_future::TimerFuture, +use futures::{ + future::{BoxFuture, FutureExt}, + task::{waker_ref, ArcWake}, }; +use std::{ + future::Future, + sync::mpsc::{sync_channel, Receiver, SyncSender}, + sync::{Arc, Mutex}, + task::Context, + time::Duration, +}; +// The timer we wrote in the previous section: +use timer_future::TimerFuture; // ANCHOR_END: imports // ANCHOR: executor_decl @@ -92,7 +90,7 @@ impl Executor { if let Some(mut future) = future_slot.take() { // Create a `LocalWaker` from the task itself let waker = waker_ref(&task); - let context = &mut Context::from_waker(&*waker); + let context = &mut Context::from_waker(&waker); // `BoxFuture` is a type alias for // `Pin + Send + 'static>>`. // We can get a `Pin<&mut dyn Future + Send + 'static>` diff --git a/examples/03_01_async_await/Cargo.toml b/examples/03_01_async_await/Cargo.toml index 2971d1ec..1a041615 100644 --- a/examples/03_01_async_await/Cargo.toml +++ b/examples/03_01_async_await/Cargo.toml @@ -2,7 +2,7 @@ name = "example_03_01_async_await" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/05_01_streams/Cargo.toml b/examples/05_01_streams/Cargo.toml index 7f997e93..08f8c3e9 100644 --- a/examples/05_01_streams/Cargo.toml +++ b/examples/05_01_streams/Cargo.toml @@ -2,7 +2,7 @@ name = "example_05_01_streams" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/05_01_streams/src/lib.rs b/examples/05_01_streams/src/lib.rs index d68fc7e3..d5603734 100644 --- a/examples/05_01_streams/src/lib.rs +++ b/examples/05_01_streams/src/lib.rs @@ -1,12 +1,10 @@ #![cfg(test)] mod stream_trait { -use { - futures::stream::{Stream as RealStream}, - std::{ - pin::Pin, - task::{Context, Poll}, - }, +use futures::stream::Stream as RealStream; +use std::{ + pin::Pin, + task::{Context, Poll}, }; // ANCHOR: stream_trait @@ -34,11 +32,9 @@ impl Stream for dyn RealStream { } mod channels { -use { - futures::{ - channel::mpsc, - prelude::*, - }, +use futures::{ + channel::mpsc, + prelude::*, }; // ANCHOR: channels diff --git a/examples/05_02_iteration_and_concurrency/Cargo.toml b/examples/05_02_iteration_and_concurrency/Cargo.toml index 4d2192b7..5bba5c95 100644 --- a/examples/05_02_iteration_and_concurrency/Cargo.toml +++ b/examples/05_02_iteration_and_concurrency/Cargo.toml @@ -2,7 +2,7 @@ name = "example_05_02_iteration_and_concurrency" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/05_02_iteration_and_concurrency/src/lib.rs b/examples/05_02_iteration_and_concurrency/src/lib.rs index 6eb36a14..71a463cd 100644 --- a/examples/05_02_iteration_and_concurrency/src/lib.rs +++ b/examples/05_02_iteration_and_concurrency/src/lib.rs @@ -1,14 +1,12 @@ #![cfg(test)] -use { - futures::{ - executor::block_on, - stream::{self, Stream}, - }, - std::{ - io, - pin::Pin, - }, +use futures::{ + executor::block_on, + stream::{self, Stream}, +}; +use std::{ + io, + pin::Pin, }; // ANCHOR: nexts diff --git a/examples/06_02_join/Cargo.toml b/examples/06_02_join/Cargo.toml index 81157f93..4048a9f8 100644 --- a/examples/06_02_join/Cargo.toml +++ b/examples/06_02_join/Cargo.toml @@ -2,7 +2,7 @@ name = "example_06_02_join" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/06_03_select/Cargo.toml b/examples/06_03_select/Cargo.toml index d53196d5..f1a53c21 100644 --- a/examples/06_03_select/Cargo.toml +++ b/examples/06_03_select/Cargo.toml @@ -2,7 +2,7 @@ name = "example_06_03_select" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/06_03_select/src/lib.rs b/examples/06_03_select/src/lib.rs index d2ac1af6..bfd93571 100644 --- a/examples/06_03_select/src/lib.rs +++ b/examples/06_03_select/src/lib.rs @@ -140,13 +140,6 @@ async fn get_new_num() -> u8 { /* ... */ 5 } async fn run_on_new_num(_: u8) -> u8 { /* ... */ 5 } -// Runs `run_on_new_num` with the latest number -// retrieved from `get_new_num`. -// -// `get_new_num` is re-run every time a timer elapses, -// immediately cancelling the currently running -// `run_on_new_num` and replacing it with the newly -// returned value. async fn run_loop( mut interval_timer: impl Stream + FusedStream + Unpin, starting_num: u8, diff --git a/examples/06_04_spawning/Cargo.toml b/examples/06_04_spawning/Cargo.toml new file mode 100644 index 00000000..e70d1b45 --- /dev/null +++ b/examples/06_04_spawning/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "example_06_04_spawning" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures = "0.3" + +[dependencies.async-std] +version = "1.12.0" +features = ["attributes"] \ No newline at end of file diff --git a/examples/06_04_spawning/src/lib.rs b/examples/06_04_spawning/src/lib.rs new file mode 100644 index 00000000..e6f7c508 --- /dev/null +++ b/examples/06_04_spawning/src/lib.rs @@ -0,0 +1,46 @@ +#![cfg(test)] +#![allow(dead_code)] + +// ANCHOR: example +use async_std::{task, net::TcpListener, net::TcpStream}; +use futures::AsyncWriteExt; + +async fn process_request(stream: &mut TcpStream) -> Result<(), std::io::Error>{ + stream.write_all(b"HTTP/1.1 200 OK\r\n\r\n").await?; + stream.write_all(b"Hello World").await?; + Ok(()) +} + +async fn main() { + let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap(); + loop { + // Accept a new connection + let (mut stream, _) = listener.accept().await.unwrap(); + // Now process this request without blocking the main loop + task::spawn(async move {process_request(&mut stream).await}); + } +} +// ANCHOR_END: example +use std::time::Duration; +async fn my_task(time: Duration) { + println!("Hello from my_task with time {:?}", time); + task::sleep(time).await; + println!("Goodbye from my_task with time {:?}", time); +} +// ANCHOR: join_all +use futures::future::join_all; +async fn task_spawner(){ + let tasks = vec![ + task::spawn(my_task(Duration::from_secs(1))), + task::spawn(my_task(Duration::from_secs(2))), + task::spawn(my_task(Duration::from_secs(3))), + ]; + // If we do not await these tasks and the function finishes, they will be dropped + join_all(tasks).await; +} +// ANCHOR_END: join_all + +#[test] +fn run_task_spawner() { + futures::executor::block_on(task_spawner()); +} \ No newline at end of file diff --git a/examples/07_05_recursion/Cargo.toml b/examples/07_05_recursion/Cargo.toml index e37fb1b5..76b87e81 100644 --- a/examples/07_05_recursion/Cargo.toml +++ b/examples/07_05_recursion/Cargo.toml @@ -2,7 +2,7 @@ name = "example_07_05_recursion" version = "0.1.0" authors = ["Taylor Cramer "] -edition = "2018" +edition = "2021" [lib] diff --git a/examples/09_01_sync_tcp_server/Cargo.toml b/examples/09_01_sync_tcp_server/Cargo.toml index 3048a1e7..47324036 100644 --- a/examples/09_01_sync_tcp_server/Cargo.toml +++ b/examples/09_01_sync_tcp_server/Cargo.toml @@ -2,7 +2,7 @@ name = "sync_tcp_server" version = "0.1.0" authors = ["Your Name + + + + Hello! + + +

Oops!

+

Sorry, I don't know what you're asking for.

+ + diff --git a/examples/09_03_slow_request/Cargo.toml b/examples/09_03_slow_request/Cargo.toml index aff14020..839c8f21 100644 --- a/examples/09_03_slow_request/Cargo.toml +++ b/examples/09_03_slow_request/Cargo.toml @@ -2,10 +2,10 @@ name = "slow_request" version = "0.1.0" authors = ["Your Name + + + + Hello! + + +

Hello!

+

Hi from Rust

+ + diff --git a/examples/09_03_slow_request/src/main.rs b/examples/09_03_slow_request/src/main.rs index 60f429c3..99786fe7 100644 --- a/examples/09_03_slow_request/src/main.rs +++ b/examples/09_03_slow_request/src/main.rs @@ -2,7 +2,6 @@ use std::fs; use std::io::{Read, Write}; use std::net::TcpListener; use std::net::TcpStream; -use std::time::Duration; #[async_std::main] async fn main() { @@ -14,6 +13,7 @@ async fn main() { } // ANCHOR: handle_connection +use std::time::Duration; use async_std::task; async fn handle_connection(mut stream: TcpStream) { diff --git a/examples/09_04_concurrent_tcp_server/Cargo.toml b/examples/09_04_concurrent_tcp_server/Cargo.toml index b4afb235..d28d348e 100644 --- a/examples/09_04_concurrent_tcp_server/Cargo.toml +++ b/examples/09_04_concurrent_tcp_server/Cargo.toml @@ -2,7 +2,7 @@ name = "concurrent_tcp_server" version = "0.1.0" authors = ["Your Name + + + + Hello! + + +

Oops!

+

Sorry, I don't know what you're asking for.

+ + diff --git a/examples/09_05_final_tcp_server/Cargo.toml b/examples/09_05_final_tcp_server/Cargo.toml index cb84f91f..21907458 100644 --- a/examples/09_05_final_tcp_server/Cargo.toml +++ b/examples/09_05_final_tcp_server/Cargo.toml @@ -2,7 +2,7 @@ name = "final_tcp_server" version = "0.1.0" authors = ["Your Name src\test.rs:56:30 + | +56 | std::mem::swap(test1.get_mut(), test2.get_mut()); + | ^^^^^^^ within `test1::Test`, the trait `Unpin` is not implemented for `PhantomPinned` + | + = note: consider using `Box::pin` +note: required because it appears within the type `test1::Test` + --> src\test.rs:7:8 + | +7 | struct Test { + | ^^^^ +note: required by a bound in `std::pin::Pin::<&'a mut T>::get_mut` + --> <...>rustlib/src/rust\library\core\src\pin.rs:748:12 + | +748 | T: Unpin, + | ^^^^^ required by this bound in `std::pin::Pin::<&'a mut T>::get_mut` +``` > It's important to note that stack pinning will always rely on guarantees > you give when writing `unsafe`. While we know that the _pointee_ of `&'a mut T` @@ -625,7 +645,7 @@ execute_unpin_future(fut); // OK ## Summary 1. If `T: Unpin` (which is the default), then `Pin<'a, T>` is entirely -equivalent to `&'a mut T`. in other words: `Unpin` means it's OK for this type +equivalent to `&'a mut T`. In other words: `Unpin` means it's OK for this type to be moved even when pinned, so `Pin` will have no effect on such a type. 2. Getting a `&mut T` to a pinned T requires unsafe if `T: !Unpin`. diff --git a/src/06_multiple_futures/04_spawning.md b/src/06_multiple_futures/04_spawning.md new file mode 100644 index 00000000..8177f0ed --- /dev/null +++ b/src/06_multiple_futures/04_spawning.md @@ -0,0 +1,24 @@ +# `Spawning` + +Spawning allows you to run a new asynchronous task in the background. This allows us to continue executing other code +while it runs. + +Say we have a web server that wants to accept connections without blocking the main thread. +To achieve this, we can use the `async_std::task::spawn` function to create and run a new task that handles the +connections. This function takes a future and returns a `JoinHandle`, which can be used to wait for the result of the +task once it's completed. + +```rust,edition2018 +{{#include ../../examples/06_04_spawning/src/lib.rs:example}} +``` + +The `JoinHandle` returned by `spawn` implements the `Future` trait, so we can `.await` it to get the result of the task. +This will block the current task until the spawned task completes. If the task is not awaited, your program will +continue executing without waiting for the task, cancelling it if the function is completed before the task is finished. + +```rust,edition2018 +{{#include ../../examples/06_04_spawning/src/lib.rs:join_all}} +``` + +To communicate between the main task and the spawned task, we can use channels +provided by the async runtime used. \ No newline at end of file diff --git a/src/07_workarounds/05_async_in_traits.md b/src/07_workarounds/05_async_in_traits.md index 7241ac37..d2c273e2 100644 --- a/src/07_workarounds/05_async_in_traits.md +++ b/src/07_workarounds/05_async_in_traits.md @@ -1,10 +1,10 @@ # `async` in Traits -Currently, `async fn` cannot be used in traits. The reasons for this are -somewhat complex, but there are plans to remove this restriction in the -future. +Currently, `async fn` cannot be used in traits on the stable release of Rust. +Since the 17th November 2022, an MVP of async-fn-in-trait is available on the nightly +version of the compiler tool chain, [see here for details](https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html). -In the meantime, however, this can be worked around using the +In the meantime, there is a work around for the stable tool chain using the [async-trait crate from crates.io](https://github.com/dtolnay/async-trait). Note that using these trait methods will result in a heap allocation diff --git a/src/09_example/03_tests.md b/src/09_example/03_tests.md index cbb9b69a..be2c0cc6 100644 --- a/src/09_example/03_tests.md +++ b/src/09_example/03_tests.md @@ -17,7 +17,6 @@ First, we'll change the signature of `handle_connection` to make it easier to te it requires any struct that implements `async_std::io::Read`, `async_std::io::Write`, and `marker::Unpin`. Changing the type signature to reflect this allows us to pass a mock for testing. ```rust,ignore -use std::marker::Unpin; use async_std::io::{Read, Write}; async fn handle_connection(mut stream: impl Read + Write + Unpin) { diff --git a/src/12_appendix/01_translations.md b/src/12_appendix/01_translations.md index 8202757c..d6fa47a8 100644 --- a/src/12_appendix/01_translations.md +++ b/src/12_appendix/01_translations.md @@ -4,3 +4,4 @@ For resources in languages other than English. - [Русский](https://doc.rust-lang.ru/async-book/) - [Français](https://jimskapt.github.io/async-book-fr/) +- [فارسی](https://rouzbehsbz.github.io/rust-async-book/) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a089ceb6..122fe1e2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -16,7 +16,7 @@ - [Executing Multiple Futures at a Time](06_multiple_futures/01_chapter.md) - [`join!`](06_multiple_futures/02_join.md) - [`select!`](06_multiple_futures/03_select.md) - - [TODO: Spawning]() + - [Spawning](06_multiple_futures/04_spawning.md) - [TODO: Cancellation and Timeouts]() - [TODO: `FuturesUnordered`]() - [Workarounds to Know and Love](07_workarounds/01_chapter.md)