├── .gitignore ├── .mds-list ├── book.toml ├── en.md ├── examples ├── 01_02_why_async │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 01_04_async_await_primer │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 01_05_http_server │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 02_02_future_trait │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 02_03_timer │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 02_04_executor │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 03_01_async_await │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 05_01_streams │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 05_02_iteration_and_concurrency │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 06_02_join │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 06_03_select │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── 07_05_recursion │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── Cargo.toml ├── readme.md ├── src ├── 01_getting_started │ ├── 01_chapter.md │ ├── 01_chapter.zh.md │ ├── 02_why_async.md │ ├── 02_why_async.zh.md │ ├── 03_state_of_async_rust.md │ ├── 03_state_of_async_rust.zh.md │ ├── 04_async_await_primer.md │ ├── 04_async_await_primer.zh.md │ ├── 05_http_server_example.md │ └── 05_http_server_example.zh.md ├── 02_execution │ ├── 01_chapter.md │ ├── 01_chapter.zh.md │ ├── 02_future.md │ ├── 02_future.zh.md │ ├── 03_wakeups.md │ ├── 03_wakeups.zh.md │ ├── 04_executor.md │ ├── 04_executor.zh.md │ ├── 05_io.md │ └── 05_io.zh.md ├── 03_async_await │ ├── 01_chapter.md │ └── 01_chapter.zh.md ├── 04_pinning │ ├── 01_chapter.md │ └── 01_chapter.zh.md ├── 05_streams │ ├── 01_chapter.md │ ├── 01_chapter.zh.md │ ├── 02_iteration_and_concurrency.md │ └── 02_iteration_and_concurrency.zh.md ├── 06_multiple_futures │ ├── 01_chapter.md │ ├── 01_chapter.zh.md │ ├── 02_join.md │ ├── 02_join.zh.md │ ├── 03_select.md │ └── 03_select.zh.md ├── 07_workarounds │ ├── 01_chapter.md │ ├── 01_chapter.zh.md │ ├── 02_return_type.md │ ├── 02_return_type.zh.md │ ├── 03_err_in_async_blocks.md │ ├── 03_err_in_async_blocks.zh.md │ ├── 04_send_approximation.md │ ├── 04_send_approximation.zh.md │ ├── 05_recursion.md │ ├── 05_recursion.zh.md │ ├── 06_async_in_traits.md │ └── 06_async_in_traits.zh.md ├── 404.md ├── SUMMARY.en.md └── SUMMARY.md ├── sync-en.sh └── theme ├── custom.css └── index.hbs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | fork 4 | source 5 | hub-create.sh -------------------------------------------------------------------------------- /.mds-list: -------------------------------------------------------------------------------- 1 | ./source/README.md 2 | ./source/src/02_execution/04_executor.md 3 | ./source/src/02_execution/01_chapter.md 4 | ./source/src/02_execution/03_wakeups.md 5 | ./source/src/02_execution/02_future.md 6 | ./source/src/02_execution/05_io.md 7 | ./source/src/04_pinning/01_chapter.md 8 | ./source/src/06_multiple_futures/01_chapter.md 9 | ./source/src/06_multiple_futures/03_select.md 10 | ./source/src/06_multiple_futures/02_join.md 11 | ./source/src/SUMMARY.md 12 | ./source/src/03_async_await/01_chapter.md 13 | ./source/src/07_workarounds/03_err_in_async_blocks.md 14 | ./source/src/07_workarounds/05_recursion.md 15 | ./source/src/07_workarounds/01_chapter.md 16 | ./source/src/07_workarounds/06_async_in_traits.md 17 | ./source/src/07_workarounds/04_send_approximation.md 18 | ./source/src/07_workarounds/02_return_type.md 19 | ./source/src/404.md 20 | ./source/src/01_getting_started/01_chapter.md 21 | ./source/src/01_getting_started/03_state_of_async_rust.md 22 | ./source/src/01_getting_started/05_http_server_example.md 23 | ./source/src/01_getting_started/04_async_await_primer.md 24 | ./source/src/01_getting_started/02_why_async.md 25 | ./source/src/05_streams/02_iteration_and_concurrency.md 26 | ./source/src/05_streams/01_chapter.md 27 | 28 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Asynchronous Programming in Rust" 3 | authors = ["Taylor Cramer", "译文:Yobrave"] 4 | description = "Rust 异步编程非官方中文." 5 | language = "zh-CN" 6 | 7 | [build] 8 | build-dir = "docs" 9 | create-missing = false 10 | use-default-preprocessors = false 11 | 12 | [preprocessor.links] 13 | # renderers = ["html"] 14 | 15 | [output.html] 16 | mathjax-support = false 17 | theme = "theme" 18 | curly-quotes = true 19 | google-analytics = "UA-128555056-1" 20 | # additional-css = ["theme/custom.css"] 21 | git_repository_url = "https://github.com/chinanf-boy/async-book-zh" 22 | git_repository_icon = "fa-github" 23 | # additional-js = ["theme/custom.js"] 24 | 25 | # [output.linkcheck] 26 | # follow-web-links = true 27 | # traverse-parent-directories = false 28 | # exclude = [ "google\\.com" ] 29 | 30 | [output.html.playpen] 31 | # editor = "./path/to/editor" 32 | editable = false 33 | 34 | [output.html.search] 35 | enable = true 36 | # searcher = "./path/to/searcher" 37 | limit-results = 30 38 | teaser-word-count = 30 39 | use-boolean-and = true 40 | boost-title = 2 41 | boost-hierarchy = 1 42 | boost-paragraph = 1 43 | expand = true 44 | heading-split-level = 3 45 | # copy-js = true -------------------------------------------------------------------------------- /en.md: -------------------------------------------------------------------------------- 1 | # async-book 2 | Asynchronous Programming in Rust 3 | 4 | ## Requirements 5 | The async book is built with [`mdbook`], you can install it using cargo. 6 | 7 | ``` 8 | cargo install mdbook 9 | ``` 10 | 11 | [`mdbook`]: https://github.com/rust-lang/mdBook 12 | 13 | ## Building 14 | To create a finished book, run `mdbook build` to generate it under the `book/` directory. 15 | ``` 16 | mdbook build 17 | ``` 18 | 19 | ## Development 20 | While writing it can be handy to see your changes, `mdbook serve` will launch a local web 21 | server to serve the book. 22 | ``` 23 | mdbook serve 24 | ``` 25 | -------------------------------------------------------------------------------- /examples/01_02_why_async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "01_02_why_async" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/01_02_why_async/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use { 4 | futures::{ 5 | executor::block_on, 6 | join, 7 | }, 8 | std::thread, 9 | }; 10 | 11 | fn download(_url: &str) { 12 | // ... 13 | } 14 | 15 | #[test] 16 | // ANCHOR: get_two_sites 17 | fn get_two_sites() { 18 | // Spawn two threads to do work. 19 | let thread_one = thread::spawn(|| download("https://www.foo.com")); 20 | let thread_two = thread::spawn(|| download("https://www.bar.com")); 21 | 22 | // Wait for both threads to complete. 23 | thread_one.join().expect("thread one panicked"); 24 | thread_two.join().expect("thread two panicked"); 25 | } 26 | // ANCHOR_END: get_two_sites 27 | 28 | async fn download_async(_url: &str) { 29 | // ... 30 | } 31 | 32 | // ANCHOR: get_two_sites_async 33 | async fn get_two_sites_async() { 34 | // Create two different "futures" which, when run to completion, 35 | // will asynchronously download the webpages. 36 | let future_one = download_async("https://www.foo.com"); 37 | let future_two = download_async("https://www.bar.com"); 38 | 39 | // Run both futures to completion at the same time. 40 | join!(future_one, future_two); 41 | } 42 | // ANCHOR_END: get_two_sites_async 43 | 44 | #[test] 45 | fn get_two_sites_async_test() { 46 | block_on(get_two_sites_async()); 47 | } 48 | -------------------------------------------------------------------------------- /examples/01_04_async_await_primer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "01_04_async_await_primer" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/01_04_async_await_primer/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use futures::executor::block_on; 4 | 5 | mod first { 6 | // ANCHOR: hello_world 7 | // `block_on` blocks the current thread until the provided future has run to 8 | // completion. Other executors provide more complex behavior, like scheduling 9 | // multiple futures onto the same thread. 10 | use futures::executor::block_on; 11 | 12 | async fn hello_world() { 13 | println!("hello, world!"); 14 | } 15 | 16 | fn main() { 17 | let future = hello_world(); // Nothing is printed 18 | block_on(future); // `future` is run and "hello, world!" is printed 19 | } 20 | // ANCHOR_END: hello_world 21 | 22 | #[test] 23 | fn run_main() { main() } 24 | } 25 | 26 | struct Song; 27 | async fn learn_song() -> Song { Song } 28 | async fn sing_song(_: Song) {} 29 | async fn dance() {} 30 | 31 | mod second { 32 | use super::*; 33 | // ANCHOR: block_on_each 34 | fn main() { 35 | let song = block_on(learn_song()); 36 | block_on(sing_song(song)); 37 | block_on(dance()); 38 | } 39 | // ANCHOR_END: block_on_each 40 | 41 | #[test] 42 | fn run_main() { main() } 43 | } 44 | 45 | mod third { 46 | use super::*; 47 | // ANCHOR: block_on_main 48 | async fn learn_and_sing() { 49 | // Wait until the song has been learned before singing it. 50 | // We use `.await` here rather than `block_on` to prevent blocking the 51 | // thread, which makes it possible to `dance` at the same time. 52 | let song = learn_song().await; 53 | sing_song(song).await; 54 | } 55 | 56 | async fn async_main() { 57 | let f1 = learn_and_sing(); 58 | let f2 = dance(); 59 | 60 | // `join!` is like `.await` but can wait for multiple futures concurrently. 61 | // If we're temporarily blocked in the `learn_and_sing` future, the `dance` 62 | // future will take over the current thread. If `dance` becomes blocked, 63 | // `learn_and_sing` can take back over. If both futures are blocked, then 64 | // `async_main` is blocked and will yield to the executor. 65 | futures::join!(f1, f2); 66 | } 67 | 68 | fn main() { 69 | block_on(async_main()); 70 | } 71 | // ANCHOR_END: block_on_main 72 | 73 | #[test] 74 | fn run_main() { main() } 75 | } 76 | -------------------------------------------------------------------------------- /examples/01_05_http_server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "01_05_http_server" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dependencies] 10 | # The latest version of the "futures" library, which has lots of utilities 11 | # for writing async code. Enable the "compat" feature to include the 12 | # functions for using futures 0.3 and async/await with the Hyper library, 13 | # which use futures 0.1. 14 | futures = { version = "0.3", features = ["compat"] } 15 | 16 | # Hyper is an asynchronous HTTP library. We'll use it to power our HTTP 17 | # server and to make HTTP requests. 18 | hyper = "0.12.9" 19 | 20 | # (only for testing) 21 | failure = "0.1.5" 22 | reqwest = "0.9.18" 23 | -------------------------------------------------------------------------------- /examples/01_05_http_server/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | // ANCHOR: imports 4 | use { 5 | hyper::{ 6 | // Miscellaneous types from Hyper for working with HTTP. 7 | Body, Client, Request, Response, Server, Uri, 8 | 9 | // This function turns a closure which returns a future into an 10 | // implementation of the the Hyper `Service` trait, which is an 11 | // asynchronous function from a generic `Request` to a `Response`. 12 | service::service_fn, 13 | 14 | // A function which runs a future to completion using the Hyper runtime. 15 | rt::run, 16 | }, 17 | futures::{ 18 | // Extension trait for futures 0.1 futures, adding the `.compat()` method 19 | // which allows us to use `.await` on 0.1 futures. 20 | compat::Future01CompatExt, 21 | // Extension traits providing additional methods on futures. 22 | // `FutureExt` adds methods that work for all futures, whereas 23 | // `TryFutureExt` adds methods to futures that return `Result` types. 24 | future::{FutureExt, TryFutureExt}, 25 | }, 26 | std::net::SocketAddr, 27 | }; 28 | // ANCHOR_END: imports 29 | 30 | // ANCHOR: boilerplate 31 | async fn serve_req(_req: Request) -> Result, hyper::Error> { 32 | // Always return successfully with a response containing a body with 33 | // a friendly greeting ;) 34 | Ok(Response::new(Body::from("hello, world!"))) 35 | } 36 | 37 | async fn run_server(addr: SocketAddr) { 38 | println!("Listening on http://{}", addr); 39 | 40 | // Create a server bound on the provided address 41 | let serve_future = Server::bind(&addr) 42 | // Serve requests using our `async serve_req` function. 43 | // `serve` takes a closure which returns a type implementing the 44 | // `Service` trait. `service_fn` returns a value implementing the 45 | // `Service` trait, and accepts a closure which goes from request 46 | // to a future of the response. To use our `serve_req` function with 47 | // Hyper, we have to box it and put it in a compatability 48 | // wrapper to go from a futures 0.3 future (the kind returned by 49 | // `async fn`) to a futures 0.1 future (the kind used by Hyper). 50 | .serve(|| service_fn(|req| serve_req(req).boxed().compat())); 51 | 52 | // Wait for the server to complete serving or exit with an error. 53 | // If an error occurred, print it to stderr. 54 | if let Err(e) = serve_future.compat().await { 55 | eprintln!("server error: {}", e); 56 | } 57 | } 58 | 59 | fn main() { 60 | // Set the address to run our socket on. 61 | let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); 62 | 63 | // Call our `run_server` function, which returns a future. 64 | // As with every `async fn`, for `run_server` to do anything, 65 | // the returned future needs to be run. Additionally, 66 | // we need to convert the returned future from a futures 0.3 future into a 67 | // futures 0.1 future. 68 | let futures_03_future = run_server(addr); 69 | let futures_01_future = futures_03_future.unit_error().boxed().compat(); 70 | 71 | // Finally, we can run the future to completion using the `run` function 72 | // provided by Hyper. 73 | run(futures_01_future); 74 | } 75 | // ANCHOR_END: boilerplate 76 | 77 | #[test] 78 | fn run_main_and_query_http() -> Result<(), failure::Error> { 79 | std::thread::spawn(|| main()); 80 | // Unfortunately, there's no good way for us to detect when the server 81 | // has come up, so we sleep for an amount that should hopefully be 82 | // sufficient :( 83 | std::thread::sleep(std::time::Duration::from_secs(5)); 84 | let response = reqwest::get("http://localhost:3000")?.text()?; 85 | assert_eq!(response, "hello, world!"); 86 | Ok(()) 87 | } 88 | 89 | mod proxy { 90 | use super::*; 91 | #[allow(unused)] 92 | async fn serve_req(_req: Request) -> Result, hyper::Error> { 93 | // ANCHOR: parse_url 94 | let url_str = "http://www.rust-lang.org/en-US/"; 95 | let url = url_str.parse::().expect("failed to parse URL"); 96 | // ANCHOR_END: parse_url 97 | 98 | // ANCHOR: get_request 99 | let res = Client::new().get(url).compat().await; 100 | // Return the result of the request directly to the user 101 | println!("request finished-- returning response"); 102 | res 103 | // ANCHOR_END: get_request 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/02_02_future_trait/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "02_02_future_trait" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | -------------------------------------------------------------------------------- /examples/02_02_future_trait/src/lib.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: simple_future 2 | trait SimpleFuture { 3 | type Output; 4 | fn poll(&mut self, wake: fn()) -> Poll; 5 | } 6 | 7 | enum Poll { 8 | Ready(T), 9 | Pending, 10 | } 11 | // ANCHOR_END: simple_future 12 | 13 | struct Socket; 14 | impl Socket { 15 | fn has_data_to_read(&self) -> bool { 16 | // check if the socket is currently readable 17 | true 18 | } 19 | fn read_buf(&self) -> Vec { 20 | // Read data in from the socket 21 | vec![] 22 | } 23 | fn set_readable_callback(&self, _wake: fn()) { 24 | // register `_wake` with something that will call it 25 | // once the socket becomes readable, such as an 26 | // `epoll`-based event loop. 27 | } 28 | } 29 | 30 | // ANCHOR: socket_read 31 | pub struct SocketRead<'a> { 32 | socket: &'a Socket, 33 | } 34 | 35 | impl SimpleFuture for SocketRead<'_> { 36 | type Output = Vec; 37 | 38 | fn poll(&mut self, wake: fn()) -> Poll { 39 | if self.socket.has_data_to_read() { 40 | // The socket has data-- read it into a buffer and return it. 41 | Poll::Ready(self.socket.read_buf()) 42 | } else { 43 | // The socket does not yet have data. 44 | // 45 | // Arrange for `wake` to be called once data is available. 46 | // When data becomes available, `wake` will be called, and the 47 | // user of this `Future` will know to call `poll` again and 48 | // receive data. 49 | self.socket.set_readable_callback(wake); 50 | Poll::Pending 51 | } 52 | } 53 | } 54 | // ANCHOR_END: socket_read 55 | 56 | // ANCHOR: join 57 | /// A SimpleFuture that runs two other futures to completion concurrently. 58 | /// 59 | /// Concurrency is achieved via the fact that calls to `poll` each future 60 | /// may be interleaved, allowing each future to advance itself at its own pace. 61 | pub struct Join { 62 | // Each field may contain a future that should be run to completion. 63 | // If the future has already completed, the field is set to `None`. 64 | // This prevents us from polling a future after it has completed, which 65 | // would violate the contract of the `Future` trait. 66 | a: Option, 67 | b: Option, 68 | } 69 | 70 | impl SimpleFuture for Join 71 | where 72 | FutureA: SimpleFuture, 73 | FutureB: SimpleFuture, 74 | { 75 | type Output = (); 76 | fn poll(&mut self, wake: fn()) -> Poll { 77 | // Attempt to complete future `a`. 78 | if let Some(a) = &mut self.a { 79 | if let Poll::Ready(()) = a.poll(wake) { 80 | self.a.take(); 81 | } 82 | } 83 | 84 | // Attempt to complete future `b`. 85 | if let Some(b) = &mut self.b { 86 | if let Poll::Ready(()) = b.poll(wake) { 87 | self.b.take(); 88 | } 89 | } 90 | 91 | if self.a.is_none() && self.b.is_none() { 92 | // Both futures have completed-- we can return successfully 93 | Poll::Ready(()) 94 | } else { 95 | // One or both futures returned `Poll::Pending` and still have 96 | // work to do. They will call `wake()` when progress can be made. 97 | Poll::Pending 98 | } 99 | } 100 | } 101 | // ANCHOR_END: join 102 | 103 | // ANCHOR: and_then 104 | /// A SimpleFuture that runs two futures to completion, one after another. 105 | // 106 | // Note: for the purposes of this simple example, `AndThenFut` assumes both 107 | // the first and second futures are available at creation-time. The real 108 | // `AndThen` combinator allows creating the second future based on the output 109 | // of the first future, like `get_breakfast.and_then(|food| eat(food))`. 110 | pub struct AndThenFut { 111 | first: Option, 112 | second: FutureB, 113 | } 114 | 115 | impl SimpleFuture for AndThenFut 116 | where 117 | FutureA: SimpleFuture, 118 | FutureB: SimpleFuture, 119 | { 120 | type Output = (); 121 | fn poll(&mut self, wake: fn()) -> Poll { 122 | if let Some(first) = &mut self.first { 123 | match first.poll(wake) { 124 | // We've completed the first future-- remove it and start on 125 | // the second! 126 | Poll::Ready(()) => self.first.take(), 127 | // We couldn't yet complete the first future. 128 | Poll::Pending => return Poll::Pending, 129 | }; 130 | } 131 | // Now that the first future is done, attempt to complete the second. 132 | self.second.poll(wake) 133 | } 134 | } 135 | // ANCHOR_END: and_then 136 | 137 | mod real_future { 138 | use std::{ 139 | future::Future as RealFuture, 140 | pin::Pin, 141 | task::{Context, Poll}, 142 | }; 143 | 144 | // ANCHOR: real_future 145 | trait Future { 146 | type Output; 147 | fn poll( 148 | // Note the change from `&mut self` to `Pin<&mut Self>`: 149 | self: Pin<&mut Self>, 150 | // and the change from `wake: fn()` to `cx: &mut Context<'_>`: 151 | cx: &mut Context<'_>, 152 | ) -> Poll; 153 | } 154 | // ANCHOR_END: real_future 155 | 156 | // ensure that `Future` matches `RealFuture`: 157 | impl Future for dyn RealFuture { 158 | type Output = O; 159 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 160 | RealFuture::poll(self, cx) 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /examples/02_03_timer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "02_03_timer" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/02_03_timer/src/lib.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: imports 2 | use { 3 | std::{ 4 | future::Future, 5 | pin::Pin, 6 | sync::{Arc, Mutex}, 7 | task::{Context, Poll, Waker}, 8 | thread, 9 | time::Duration, 10 | }, 11 | }; 12 | // ANCHOR_END: imports 13 | 14 | // ANCHOR: timer_decl 15 | pub struct TimerFuture { 16 | shared_state: Arc>, 17 | } 18 | 19 | /// Shared state between the future and the waiting thread 20 | struct SharedState { 21 | /// Whether or not the sleep time has elapsed 22 | completed: bool, 23 | 24 | /// The waker for the task that `TimerFuture` is running on. 25 | /// The thread can use this after setting `completed = true` to tell 26 | /// `TimerFuture`'s task to wake up, see that `completed = true`, and 27 | /// move forward. 28 | waker: Option, 29 | } 30 | // ANCHOR_END: timer_decl 31 | 32 | // ANCHOR: future_for_timer 33 | impl Future for TimerFuture { 34 | type Output = (); 35 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 36 | // Look at the shared state to see if the timer has already completed. 37 | let mut shared_state = self.shared_state.lock().unwrap(); 38 | if shared_state.completed { 39 | Poll::Ready(()) 40 | } else { 41 | // Set waker so that the thread can wake up the current task 42 | // when the timer has completed, ensuring that the future is polled 43 | // again and sees that `completed = true`. 44 | // 45 | // It's tempting to do this once rather than repeatedly cloning 46 | // the waker each time. However, the `TimerFuture` can move between 47 | // tasks on the executor, which could cause a stale waker pointing 48 | // to the wrong task, preventing `TimerFuture` from waking up 49 | // correctly. 50 | // 51 | // N.B. it's possible to check for this using the `Waker::will_wake` 52 | // function, but we omit that here to keep things simple. 53 | shared_state.waker = Some(cx.waker().clone()); 54 | Poll::Pending 55 | } 56 | } 57 | } 58 | // ANCHOR_END: future_for_timer 59 | 60 | // ANCHOR: timer_new 61 | impl TimerFuture { 62 | /// Create a new `TimerFuture` which will complete after the provided 63 | /// timeout. 64 | pub fn new(duration: Duration) -> Self { 65 | let shared_state = Arc::new(Mutex::new(SharedState { 66 | completed: false, 67 | waker: None, 68 | })); 69 | 70 | // Spawn the new thread 71 | let thread_shared_state = shared_state.clone(); 72 | thread::spawn(move || { 73 | thread::sleep(duration); 74 | let mut shared_state = thread_shared_state.lock().unwrap(); 75 | // Signal that the timer has completed and wake up the last 76 | // task on which the future was polled, if one exists. 77 | shared_state.completed = true; 78 | if let Some(waker) = shared_state.waker.take() { 79 | waker.wake() 80 | } 81 | }); 82 | 83 | TimerFuture { shared_state } 84 | } 85 | } 86 | // ANCHOR_END: timer_new 87 | 88 | #[test] 89 | fn block_on_timer() { 90 | futures::executor::block_on(async { 91 | TimerFuture::new(Duration::from_secs(1)).await 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /examples/02_04_executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "02_04_executor" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dependencies] 10 | futures = "0.3" 11 | timer_future = { package = "02_03_timer", path = "../02_03_timer" } 12 | -------------------------------------------------------------------------------- /examples/02_04_executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | // ANCHOR: imports 4 | use { 5 | futures::{ 6 | future::{FutureExt, BoxFuture}, 7 | task::{ArcWake, waker_ref}, 8 | }, 9 | std::{ 10 | future::Future, 11 | sync::{Arc, Mutex}, 12 | sync::mpsc::{sync_channel, SyncSender, Receiver}, 13 | task::{Context, Poll}, 14 | time::Duration, 15 | }, 16 | // The timer we wrote in the previous section: 17 | timer_future::TimerFuture, 18 | }; 19 | // ANCHOR_END: imports 20 | 21 | // ANCHOR: executor_decl 22 | /// Task executor that receives tasks off of a channel and runs them. 23 | struct Executor { 24 | ready_queue: Receiver>, 25 | } 26 | 27 | /// `Spawner` spawns new futures onto the task channel. 28 | #[derive(Clone)] 29 | struct Spawner { 30 | task_sender: SyncSender>, 31 | } 32 | 33 | /// A future that can reschedule itself to be polled by an `Executor`. 34 | struct Task { 35 | /// In-progress future that should be pushed to completion. 36 | /// 37 | /// The `Mutex` is not necessary for correctness, since we only have 38 | /// one thread executing tasks at once. However, Rust isn't smart 39 | /// enough to know that `future` is only mutated from one thread, 40 | /// so we need use the `Mutex` to prove thread-safety. A production 41 | /// executor would not need this, and could use `UnsafeCell` instead. 42 | future: Mutex>>, 43 | 44 | /// Handle to place the task itself back onto the task queue. 45 | task_sender: SyncSender>, 46 | } 47 | 48 | fn new_executor_and_spawner() -> (Executor, Spawner) { 49 | // Maximum number of tasks to allow queueing in the channel at once. 50 | // This is just to make `sync_channel` happy, and wouldn't be present in 51 | // a real executor. 52 | const MAX_QUEUED_TASKS: usize = 10_000; 53 | let (task_sender, ready_queue) = sync_channel(MAX_QUEUED_TASKS); 54 | (Executor { ready_queue }, Spawner { task_sender }) 55 | } 56 | // ANCHOR_END: executor_decl 57 | 58 | // ANCHOR: spawn_fn 59 | impl Spawner { 60 | fn spawn(&self, future: impl Future + 'static + Send) { 61 | let future = future.boxed(); 62 | let task = Arc::new(Task { 63 | future: Mutex::new(Some(future)), 64 | task_sender: self.task_sender.clone(), 65 | }); 66 | self.task_sender.send(task).expect("too many tasks queued"); 67 | } 68 | } 69 | // ANCHOR_END: spawn_fn 70 | 71 | // ANCHOR: arcwake_for_task 72 | impl ArcWake for Task { 73 | fn wake_by_ref(arc_self: &Arc) { 74 | // Implement `wake` by sending this task back onto the task channel 75 | // so that it will be polled again by the executor. 76 | let cloned = arc_self.clone(); 77 | arc_self.task_sender.send(cloned).expect("too many tasks queued"); 78 | } 79 | } 80 | // ANCHOR_END: arcwake_for_task 81 | 82 | // ANCHOR: executor_run 83 | impl Executor { 84 | fn run(&self) { 85 | while let Ok(task) = self.ready_queue.recv() { 86 | // Take the future, and if it has not yet completed (is still Some), 87 | // poll it in an attempt to complete it. 88 | let mut future_slot = task.future.lock().unwrap(); 89 | if let Some(mut future) = future_slot.take() { 90 | // Create a `LocalWaker` from the task itself 91 | let waker = waker_ref(&task); 92 | let context = &mut Context::from_waker(&*waker); 93 | // `BoxFuture` is a type alias for 94 | // `Pin + Send + 'static>>`. 95 | // We can get a `Pin<&mut dyn Future + Send + 'static>` 96 | // from it by calling the `Pin::as_mut` method. 97 | if let Poll::Pending = future.as_mut().poll(context) { 98 | // We're not done processing the future, so put it 99 | // back in its task to be run again in the future. 100 | *future_slot = Some(future); 101 | } 102 | } 103 | } 104 | } 105 | } 106 | // ANCHOR_END: executor_run 107 | 108 | // ANCHOR: main 109 | fn main() { 110 | let (executor, spawner) = new_executor_and_spawner(); 111 | 112 | // Spawn a task to print before and after waiting on a timer. 113 | spawner.spawn(async { 114 | println!("howdy!"); 115 | // Wait for our timer future to complete after two seconds. 116 | TimerFuture::new(Duration::new(2, 0)).await; 117 | println!("done!"); 118 | }); 119 | 120 | // Drop the spawner so that our executor knows it is finished and won't 121 | // receive more incoming tasks to run. 122 | drop(spawner); 123 | 124 | // Run the executor until the task queue is empty. 125 | // This will print "howdy!", pause, and then print "done!". 126 | executor.run(); 127 | } 128 | // ANCHOR_END: main 129 | 130 | #[test] 131 | fn run_main() { main() } 132 | -------------------------------------------------------------------------------- /examples/03_01_async_await/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "03_01_async_await" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/03_01_async_await/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | #![cfg(test)] 3 | 4 | mod async_fn_and_block_examples { 5 | use std::future::Future; 6 | // ANCHOR: async_fn_and_block_examples 7 | 8 | // `foo()` returns a type that implements `Future`. 9 | // `foo().await` will result in a value of type `u8`. 10 | async fn foo() -> u8 { 5 } 11 | 12 | fn bar() -> impl Future { 13 | // This `async` block results in a type that implements 14 | // `Future`. 15 | async { 16 | let x: u8 = foo().await; 17 | x + 5 18 | } 19 | } 20 | // ANCHOR_END: async_fn_and_block_examples 21 | } 22 | 23 | mod async_lifetimes_examples { 24 | use std::future::Future; 25 | // ANCHOR: lifetimes_expanded 26 | // This function: 27 | async fn foo(x: &u8) -> u8 { *x } 28 | 29 | // Is equivalent to this function: 30 | fn foo_expanded<'a>(x: &'a u8) -> impl Future + 'a { 31 | async move { *x } 32 | } 33 | // ANCHOR_END: lifetimes_expanded 34 | 35 | async fn borrow_x(x: &u8) -> u8 { *x } 36 | 37 | #[cfg(feature = "never_compiled")] 38 | // ANCHOR: static_future_with_borrow 39 | fn bad() -> impl Future { 40 | let x = 5; 41 | borrow_x(&x) // ERROR: `x` does not live long enough 42 | } 43 | 44 | fn good() -> impl Future { 45 | async { 46 | let x = 5; 47 | borrow_x(&x).await 48 | } 49 | } 50 | // ANCHOR_END: static_future_with_borrow 51 | } 52 | 53 | mod async_move_examples { 54 | use std::future::Future; 55 | // ANCHOR: async_move_examples 56 | /// `async` block: 57 | /// 58 | /// Multiple different `async` blocks can access the same local variable 59 | /// so long as they're executed within the variable's scope 60 | async fn blocks() { 61 | let my_string = "foo".to_string(); 62 | 63 | let future_one = async { 64 | // ... 65 | println!("{}", my_string); 66 | }; 67 | 68 | let future_two = async { 69 | // ... 70 | println!("{}", my_string); 71 | }; 72 | 73 | // Run both futures to completion, printing "foo" twice: 74 | let ((), ()) = futures::join!(future_one, future_two); 75 | } 76 | 77 | /// `async move` block: 78 | /// 79 | /// Only one `async move` block can access the same captured variable, since 80 | /// captures are moved into the `Future` generated by the `async move` block. 81 | /// However, this allows the `Future` to outlive the original scope of the 82 | /// variable: 83 | fn move_block() -> impl Future { 84 | let my_string = "foo".to_string(); 85 | async move { 86 | // ... 87 | println!("{}", my_string); 88 | } 89 | } 90 | // ANCHOR_END: async_move_examples 91 | } 92 | -------------------------------------------------------------------------------- /examples/05_01_streams/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "05_01_streams" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/05_01_streams/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | mod stream_trait { 4 | use { 5 | futures::stream::{Stream as RealStream}, 6 | std::{ 7 | pin::Pin, 8 | task::{Context, Poll}, 9 | }, 10 | }; 11 | 12 | // ANCHOR: stream_trait 13 | trait Stream { 14 | /// The type of the value yielded by the stream. 15 | type Item; 16 | 17 | /// Attempt to resolve the next item in the stream. 18 | /// Retuns `Poll::Pending` if not ready, `Poll::Ready(Some(x))` if a value 19 | /// is ready, and `Poll::Ready(None)` if the stream has completed. 20 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) 21 | -> Poll>; 22 | } 23 | // ANCHOR_END: stream_trait 24 | 25 | // assert that `Stream` matches `RealStream`: 26 | impl Stream for dyn RealStream { 27 | type Item = I; 28 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) 29 | -> Poll> 30 | { 31 | RealStream::poll_next(self, cx) 32 | } 33 | } 34 | } 35 | 36 | mod channels { 37 | use { 38 | futures::{ 39 | channel::mpsc, 40 | prelude::*, 41 | }, 42 | }; 43 | 44 | // ANCHOR: channels 45 | async fn send_recv() { 46 | const BUFFER_SIZE: usize = 10; 47 | let (mut tx, mut rx) = mpsc::channel::(BUFFER_SIZE); 48 | 49 | tx.send(1).await.unwrap(); 50 | tx.send(2).await.unwrap(); 51 | drop(tx); 52 | 53 | // `StreamExt::next` is similar to `Iterator::next`, but returns a 54 | // type that implements `Future>`. 55 | assert_eq!(Some(1), rx.next().await); 56 | assert_eq!(Some(2), rx.next().await); 57 | assert_eq!(None, rx.next().await); 58 | } 59 | // ANCHOR_END: channels 60 | 61 | #[test] 62 | fn run_send_recv() { futures::executor::block_on(send_recv()) } 63 | } 64 | -------------------------------------------------------------------------------- /examples/05_02_iteration_and_concurrency/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "05_02_iteration_and_concurrency" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/05_02_iteration_and_concurrency/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use { 4 | futures::{ 5 | executor::block_on, 6 | stream::{self, Stream}, 7 | }, 8 | std::{ 9 | io, 10 | pin::Pin, 11 | }, 12 | }; 13 | 14 | // ANCHOR: nexts 15 | async fn sum_with_next(mut stream: Pin<&mut dyn Stream>) -> i32 { 16 | use futures::stream::StreamExt; // for `next` 17 | let mut sum = 0; 18 | while let Some(item) = stream.next().await { 19 | sum += item; 20 | } 21 | sum 22 | } 23 | 24 | async fn sum_with_try_next( 25 | mut stream: Pin<&mut dyn Stream>>, 26 | ) -> Result { 27 | use futures::stream::TryStreamExt; // for `try_next` 28 | let mut sum = 0; 29 | while let Some(item) = stream.try_next().await? { 30 | sum += item; 31 | } 32 | Ok(sum) 33 | } 34 | // ANCHOR_END: nexts 35 | 36 | #[test] 37 | fn run_sum_with_next() { 38 | let mut stream = stream::iter(vec![2, 3]); 39 | let pin: Pin<&mut stream::Iter<_>> = Pin::new(&mut stream); 40 | assert_eq!(5, block_on(sum_with_next(pin))); 41 | } 42 | 43 | #[test] 44 | fn run_sum_with_try_next() { 45 | let mut stream = stream::iter(vec![Ok(2), Ok(3)]); 46 | let pin: Pin<&mut stream::Iter<_>> = Pin::new(&mut stream); 47 | assert_eq!(5, block_on(sum_with_try_next(pin)).unwrap()); 48 | } 49 | 50 | #[allow(unused)] 51 | // ANCHOR: try_for_each_concurrent 52 | async fn jump_around( 53 | mut stream: Pin<&mut dyn Stream>>, 54 | ) -> Result<(), io::Error> { 55 | use futures::stream::TryStreamExt; // for `try_for_each_concurrent` 56 | const MAX_CONCURRENT_JUMPERS: usize = 100; 57 | 58 | stream.try_for_each_concurrent(MAX_CONCURRENT_JUMPERS, |num| async move { 59 | jump_n_times(num).await?; 60 | report_n_jumps(num).await?; 61 | Ok(()) 62 | }).await?; 63 | 64 | Ok(()) 65 | } 66 | // ANCHOR_END: try_for_each_concurrent 67 | 68 | async fn jump_n_times(_: u8) -> Result<(), io::Error> { Ok(()) } 69 | async fn report_n_jumps(_: u8) -> Result<(), io::Error> { Ok(()) } 70 | -------------------------------------------------------------------------------- /examples/06_02_join/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "06_02_join" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/06_02_join/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | struct Book; 4 | struct Music; 5 | async fn get_book() -> Book { Book } 6 | async fn get_music() -> Music { Music } 7 | 8 | mod naiive { 9 | use super::*; 10 | // ANCHOR: naiive 11 | async fn get_book_and_music() -> (Book, Music) { 12 | let book = get_book().await; 13 | let music = get_music().await; 14 | (book, music) 15 | } 16 | // ANCHOR_END: naiive 17 | } 18 | 19 | mod other_langs { 20 | use super::*; 21 | // ANCHOR: other_langs 22 | // WRONG -- don't do this 23 | async fn get_book_and_music() -> (Book, Music) { 24 | let book_future = get_book(); 25 | let music_future = get_music(); 26 | (book_future.await, music_future.await) 27 | } 28 | // ANCHOR_END: other_langs 29 | } 30 | 31 | mod join { 32 | use super::*; 33 | // ANCHOR: join 34 | use futures::join; 35 | 36 | async fn get_book_and_music() -> (Book, Music) { 37 | let book_fut = get_book(); 38 | let music_fut = get_music(); 39 | join!(book_fut, music_fut) 40 | } 41 | // ANCHOR_END: join 42 | } 43 | 44 | mod try_join { 45 | use super::{Book, Music}; 46 | // ANCHOR: try_join 47 | use futures::try_join; 48 | 49 | async fn get_book() -> Result { /* ... */ Ok(Book) } 50 | async fn get_music() -> Result { /* ... */ Ok(Music) } 51 | 52 | async fn get_book_and_music() -> Result<(Book, Music), String> { 53 | let book_fut = get_book(); 54 | let music_fut = get_music(); 55 | try_join!(book_fut, music_fut) 56 | } 57 | // ANCHOR_END: try_join 58 | } 59 | 60 | mod mismatched_err { 61 | use super::{Book, Music}; 62 | // ANCHOR: try_join_map_err 63 | use futures::{ 64 | future::TryFutureExt, 65 | try_join, 66 | }; 67 | 68 | async fn get_book() -> Result { /* ... */ Ok(Book) } 69 | async fn get_music() -> Result { /* ... */ Ok(Music) } 70 | 71 | async fn get_book_and_music() -> Result<(Book, Music), String> { 72 | let book_fut = get_book().map_err(|()| "Unable to get book".to_string()); 73 | let music_fut = get_music(); 74 | try_join!(book_fut, music_fut) 75 | } 76 | // ANCHOR_END: try_join_map_err 77 | } 78 | -------------------------------------------------------------------------------- /examples/06_03_select/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "06_03_select" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/06_03_select/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | #![recursion_limit="128"] 3 | 4 | mod example { 5 | // ANCHOR: example 6 | use futures::{ 7 | future::FutureExt, // for `.fuse()` 8 | pin_mut, 9 | select, 10 | }; 11 | 12 | async fn task_one() { /* ... */ } 13 | async fn task_two() { /* ... */ } 14 | 15 | async fn race_tasks() { 16 | let t1 = task_one().fuse(); 17 | let t2 = task_two().fuse(); 18 | 19 | pin_mut!(t1, t2); 20 | 21 | select! { 22 | () = t1 => println!("task one completed first"), 23 | () = t2 => println!("task two completed first"), 24 | } 25 | } 26 | // ANCHOR_END: example 27 | } 28 | 29 | mod default_and_complete { 30 | // ANCHOR: default_and_complete 31 | use futures::{future, select}; 32 | 33 | async fn count() { 34 | let mut a_fut = future::ready(4); 35 | let mut b_fut = future::ready(6); 36 | let mut total = 0; 37 | 38 | loop { 39 | select! { 40 | a = a_fut => total += a, 41 | b = b_fut => total += b, 42 | complete => break, 43 | default => unreachable!(), // never runs (futures are ready, then complete) 44 | }; 45 | } 46 | assert_eq!(total, 10); 47 | } 48 | // ANCHOR_END: default_and_complete 49 | 50 | #[test] 51 | fn run_count() { 52 | futures::executor::block_on(count()); 53 | } 54 | } 55 | 56 | mod fused_stream { 57 | // ANCHOR: fused_stream 58 | use futures::{ 59 | stream::{Stream, StreamExt, FusedStream}, 60 | select, 61 | }; 62 | 63 | async fn add_two_streams( 64 | mut s1: impl Stream + FusedStream + Unpin, 65 | mut s2: impl Stream + FusedStream + Unpin, 66 | ) -> u8 { 67 | let mut total = 0; 68 | 69 | loop { 70 | let item = select! { 71 | x = s1.next() => x, 72 | x = s2.next() => x, 73 | complete => break, 74 | }; 75 | if let Some(next_num) = item { 76 | total += next_num; 77 | } 78 | } 79 | 80 | total 81 | } 82 | // ANCHOR_END: fused_stream 83 | } 84 | 85 | mod fuse_terminated { 86 | // ANCHOR: fuse_terminated 87 | use futures::{ 88 | future::{Fuse, FusedFuture, FutureExt}, 89 | stream::{FusedStream, Stream, StreamExt}, 90 | pin_mut, 91 | select, 92 | }; 93 | 94 | async fn get_new_num() -> u8 { /* ... */ 5 } 95 | 96 | async fn run_on_new_num(_: u8) { /* ... */ } 97 | 98 | async fn run_loop( 99 | mut interval_timer: impl Stream + FusedStream + Unpin, 100 | starting_num: u8, 101 | ) { 102 | let run_on_new_num_fut = run_on_new_num(starting_num).fuse(); 103 | let get_new_num_fut = Fuse::terminated(); 104 | pin_mut!(run_on_new_num_fut, get_new_num_fut); 105 | loop { 106 | select! { 107 | () = interval_timer.select_next_some() => { 108 | // The timer has elapsed. Start a new `get_new_num_fut` 109 | // if one was not already running. 110 | if get_new_num_fut.is_terminated() { 111 | get_new_num_fut.set(get_new_num().fuse()); 112 | } 113 | }, 114 | new_num = get_new_num_fut => { 115 | // A new number has arrived-- start a new `run_on_new_num_fut`, 116 | // dropping the old one. 117 | run_on_new_num_fut.set(run_on_new_num(new_num).fuse()); 118 | }, 119 | // Run the `run_on_new_num_fut` 120 | () = run_on_new_num_fut => {}, 121 | // panic if everything completed, since the `interval_timer` should 122 | // keep yielding values indefinitely. 123 | complete => panic!("`interval_timer` completed unexpectedly"), 124 | } 125 | } 126 | } 127 | // ANCHOR_END: fuse_terminated 128 | } 129 | 130 | mod futures_unordered { 131 | // ANCHOR: futures_unordered 132 | use futures::{ 133 | future::{Fuse, FusedFuture, FutureExt}, 134 | stream::{FusedStream, FuturesUnordered, Stream, StreamExt}, 135 | pin_mut, 136 | select, 137 | }; 138 | 139 | async fn get_new_num() -> u8 { /* ... */ 5 } 140 | 141 | async fn run_on_new_num(_: u8) -> u8 { /* ... */ 5 } 142 | 143 | // Runs `run_on_new_num` with the latest number 144 | // retrieved from `get_new_num`. 145 | // 146 | // `get_new_num` is re-run every time a timer elapses, 147 | // immediately cancelling the currently running 148 | // `run_on_new_num` and replacing it with the newly 149 | // returned value. 150 | async fn run_loop( 151 | mut interval_timer: impl Stream + FusedStream + Unpin, 152 | starting_num: u8, 153 | ) { 154 | let mut run_on_new_num_futs = FuturesUnordered::new(); 155 | run_on_new_num_futs.push(run_on_new_num(starting_num)); 156 | let get_new_num_fut = Fuse::terminated(); 157 | pin_mut!(get_new_num_fut); 158 | loop { 159 | select! { 160 | () = interval_timer.select_next_some() => { 161 | // The timer has elapsed. Start a new `get_new_num_fut` 162 | // if one was not already running. 163 | if get_new_num_fut.is_terminated() { 164 | get_new_num_fut.set(get_new_num().fuse()); 165 | } 166 | }, 167 | new_num = get_new_num_fut => { 168 | // A new number has arrived-- start a new `run_on_new_num_fut`. 169 | run_on_new_num_futs.push(run_on_new_num(new_num)); 170 | }, 171 | // Run the `run_on_new_num_futs` and check if any have completed 172 | res = run_on_new_num_futs.select_next_some() => { 173 | println!("run_on_new_num_fut returned {:?}", res); 174 | }, 175 | // panic if everything completed, since the `interval_timer` should 176 | // keep yielding values indefinitely. 177 | complete => panic!("`interval_timer` completed unexpectedly"), 178 | } 179 | } 180 | } 181 | 182 | // ANCHOR_END: futures_unordered 183 | } 184 | -------------------------------------------------------------------------------- /examples/07_05_recursion/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "07_05_recursion" 3 | version = "0.1.0" 4 | authors = ["Taylor Cramer "] 5 | edition = "2018" 6 | 7 | [lib] 8 | 9 | [dev-dependencies] 10 | futures = "0.3" 11 | -------------------------------------------------------------------------------- /examples/07_05_recursion/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | #![allow(dead_code)] 3 | 4 | // ANCHOR: example 5 | use futures::future::{BoxFuture, FutureExt}; 6 | 7 | fn recursive() -> BoxFuture<'static, ()> { 8 | async move { 9 | recursive().await; 10 | recursive().await; 11 | }.boxed() 12 | } 13 | // ANCHOR_END: example 14 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "01_02_why_async", 4 | "01_04_async_await_primer", 5 | "01_05_http_server", 6 | "02_02_future_trait", 7 | "02_03_timer", 8 | "02_04_executor", 9 | "03_01_async_await", 10 | "05_01_streams", 11 | "05_02_iteration_and_concurrency", 12 | "06_02_join", 13 | "06_03_select", 14 | "07_05_recursion", 15 | ] 16 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # rust-lang/async-book [![explain]][source] [![translate-svg]][translate-list] 2 | 3 | 4 | 5 | [explain]: http://llever.com/explain.svg 6 | [source]: https://github.com/chinanf-boy/Source-Explain 7 | [translate-svg]: http://llever.com/translate.svg 8 | [translate-list]: https://github.com/chinanf-boy/chinese-translate-list 9 | [size-img]: https://packagephobia.now.sh/badge?p=Name 10 | [size]: https://packagephobia.now.sh/result?p=Name 11 | 12 | 「 Rust 中的异步编程 」 13 | 14 | [中文](./readme.md) | [english](https://github.com/rust-lang/async-book) 15 | 16 | --- 17 | 18 | ## 校对 ✅ 19 | 20 | 21 | 22 | 23 | 24 | 25 | | 翻译的原文 | 与日期 | 最新更新 | 更多 | 26 | | ---------- | ------------- | -------- | -------------------------- | 27 | | [commit] | ⏰ 2019-11-08 | ![last] | [中文翻译][translate-list] | 28 | 29 | [last]: https://img.shields.io/github/last-commit/rust-lang/async-book.svg 30 | [commit]: https://github.com/rust-lang/async-book/tree/fa3228386e9efe4e98d222394fca6b7311e7c405 31 | 32 | 33 | 34 | - [x] [src/SUMMARY.md](src/SUMMARY.md) 35 | - [x] [入门](src/01_getting_started/01_chapter.zh.md) 36 | - [x] [为什么要 async ?](src/01_getting_started/02_why_async.zh.md) 37 | - [x] [async Rust 状态](src/01_getting_started/03_state_of_async_rust.zh.md) 38 | - [x] [`async`/`.await` Primer](src/01_getting_started/04_async_await_primer.zh.md) 39 | - [x] [应用:HTTP 服务器](src/01_getting_started/05_http_server_example.zh.md) 40 | - [x] [幕后:执行`Future`和任务](src/02_execution/01_chapter.zh.md) 41 | - [x] [`Future` Trait](src/02_execution/02_future.zh.md) 42 | - [x] [任务唤醒`Waker`](src/02_execution/03_wakeups.zh.md) 43 | - [x] [应用:生成一个执行器](src/02_execution/04_executor.zh.md) 44 | - [x] [执行器和系统 IO](src/02_execution/05_io.zh.md) 45 | - [x] [`async`/`await`](src/03_async_await/01_chapter.zh.md) 46 | - [x] [Pinning](src/04_pinning/01_chapter.zh.md) 47 | - [x] [Streams](src/05_streams/01_chapter.zh.md) 48 | - [x] [迭代与并发](src/05_streams/02_iteration_and_concurrency.zh.md) 49 | - [x] [一次执行多个 Futures](src/06_multiple_futures/01_chapter.zh.md) 50 | - [x] [`join!`](src/06_multiple_futures/02_join.zh.md) 51 | - [x] [`select!`](src/06_multiple_futures/03_select.zh.md) 52 | - [ ] [TODO: Spawning](src/404.zh.md) 53 | - [ ] [TODO:取消和超时](src/404.zh.md) 54 | - [ ] [TODO:`FuturesUnordered`](src/404.zh.md) 55 | - [x] [走走看看,想想](src/07_workarounds/01_chapter.zh.md) 56 | - [x] [返回类型的错误](src/07_workarounds/02_return_type.zh.md) 57 | - [x] [`?`在`async`代码块](src/07_workarounds/03_err_in_async_blocks.zh.md) 58 | - [x] [`Send`估计](src/07_workarounds/04_send_approximation.zh.md) 59 | - [x] [递归](src/07_workarounds/05_recursion.zh.md) 60 | - [x] [`async`在 Traits 上](src/07_workarounds/06_async_in_traits.zh.md) 61 | - [ ] [TODO:I/O](src/404.zh.md) 62 | - [ ] [TODO:`AsyncRead`以及`AsyncWrite`](src/404.zh.md) 63 | - [ ] [TODO:async 设计模式:解决方案和建议](src/404.zh.md) 64 | - [ ] [TODO:建模服务器和请求/响应模式](src/404.zh.md) 65 | - [ ] [TODO:管理共享状态](src/404.zh.md) 66 | - [ ] [TODO: 生态系统:Tokio 等](src/404.zh.md) 67 | - [ ] [TODO: 多多,多得多的东西?...](src/404.zh.md) 68 | 69 | ### 贡献 70 | 71 | 欢迎 👏 勘误/校对/更新贡献 😊 [具体贡献请看](https://github.com/chinanf-boy/chinese-translate-list#贡献) 72 | 73 | ## 生活 74 | 75 | [If help, **buy** me coffee —— 营养跟不上了,给我来瓶营养快线吧! 💰](https://github.com/chinanf-boy/live-need-money) 76 | 77 | --- 78 | 79 | # async-book 80 | 81 | Rust 中的异步编程 82 | 83 | ## 要求 84 | 85 | async-book 的构建需要[`mdbook`],您可以使用 Cargo 进行安装。 86 | 87 | ``` 88 | cargo install mdbook 89 | ``` 90 | 91 | [`mdbook`]: https://github.com/rust-lang/mdBook 92 | 93 | ## 建造 94 | 95 | 要创建完成的书,请运行`mdbook build`在下生成它`book/`目录。 96 | 97 | ``` 98 | mdbook build 99 | ``` 100 | 101 | ## 发展历程 102 | 103 | 在编写过程中,查看更改非常方便,`mdbook serve`将启动本地网络服务器来提供图书。 104 | 105 | ``` 106 | mdbook serve 107 | ``` 108 | -------------------------------------------------------------------------------- /src/01_getting_started/01_chapter.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | Welcome to Asynchronous Programming in Rust! If you're looking to start writing 4 | asynchronous Rust code, you've come to the right place. Whether you're building 5 | a web server, a database, or an operating system, this book will show you 6 | how to use Rust's asynchronous programming tools to get the most out of your 7 | hardware. 8 | 9 | ## What This Book Covers 10 | 11 | This book aims to be a comprehensive, up-to-date guide to using Rust's async 12 | language features and libraries, appropriate for beginners and old hands alike. 13 | 14 | - The early chapters provide an introduction to async programming in general, 15 | and to Rust's particular take on it. 16 | 17 | - The middle chapters discuss key utilities and control-flow tools you can use 18 | when writing async code, and describe best-practices for structuring libraries 19 | and applications to maximize performance and reusability. 20 | 21 | - The last section of the book covers the broader async ecosystem, and provides 22 | a number of examples of how to accomplish common tasks. 23 | 24 | With that out of the way, let's explore the exciting world of Asynchronous 25 | Programming in Rust! 26 | -------------------------------------------------------------------------------- /src/01_getting_started/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | 欢迎来到 Rust 异步编程!如果您打算开始编写异步 Rust 代码,那么您来对地方了。无论您是构建 Web 服务器,数据库还是操作系统,这本书都会向您展示如何使用 Rust 的异步编程工具,来充分利用你的硬件。 4 | 5 | ## 本书的覆盖面 6 | 7 | 本书旨在成为使用 Rust 异步语言功能和库的,全面的,最新指南,适用于初学者和老手。 8 | 9 | - 前面的章节对异步编程进行了总体介绍,并介绍了 Rust 的特殊用法。 10 | 11 | - 中间章节讨论了编写异步代码时,可以使用的关键实用程序和控制流工具,并描述了,对库和应用程序的构建,以最大化性能和可重用性的最佳实践。 12 | 13 | - 本书的最后一部分涵盖了更广泛的异步生态系统,并提供了一些有关如何完成常见任务的示例。 14 | 15 | 有了这些,让我们探索 Rust 异步编程的激动人心的世界! 16 | -------------------------------------------------------------------------------- /src/01_getting_started/02_why_async.md: -------------------------------------------------------------------------------- 1 | # Why Async? 2 | 3 | We all love how Rust allows us to write fast, safe software. But why write 4 | asynchronous code? 5 | 6 | Asynchronous code allows us to run multiple tasks concurrently on the same OS 7 | thread. In a typical threaded application, if you wanted to download two 8 | different webpages at the same time, you would spread the work across two 9 | different threads, like this: 10 | 11 | ```rust 12 | {{#include ../../examples/01_02_why_async/src/lib.rs:get_two_sites}} 13 | ``` 14 | 15 | This works fine for many applications-- after all, threads were designed 16 | to do just this: run multiple different tasks at once. However, they also 17 | come with some limitations. There's a lot of overhead involved in the 18 | process of switching between different threads and sharing data between 19 | threads. Even a thread which just sits and does nothing uses up valuable 20 | system resources. These are the costs that asynchronous code is designed 21 | to eliminate. We can rewrite the function above using Rust's 22 | `async`/`.await` notation, which will allow us to run multiple tasks at 23 | once without creating multiple threads: 24 | 25 | ```rust 26 | {{#include ../../examples/01_02_why_async/src/lib.rs:get_two_sites_async}} 27 | ``` 28 | 29 | Overall, asynchronous applications have the potential to be much faster and 30 | use fewer resources than a corresponding threaded implementation. However, 31 | there is a cost. Threads are natively supported by the operating system, 32 | and using them doesn't require any special programming model-- any function 33 | can create a thread, and calling a function that uses threads is usually 34 | just as easy as calling any normal function. However, asynchronous functions 35 | require special support from the language or libraries. 36 | In Rust, `async fn` creates an asynchronous function which returns a `Future`. 37 | To execute the body of the function, the returned `Future` must be run to 38 | completion. 39 | 40 | It's important to remember that traditional threaded applications can be quite 41 | effective, and that Rust's small memory footprint and predictability mean that 42 | you can get far without ever using `async`. The increased complexity of the 43 | asynchronous programming model isn't always worth it, and it's important to 44 | consider whether your application would be better served by using a simpler 45 | threaded model. 46 | -------------------------------------------------------------------------------- /src/01_getting_started/02_why_async.zh.md: -------------------------------------------------------------------------------- 1 | # Why Async? 2 | 3 | 我们都喜欢 Rust 允许我们编写快速,安全的软件。但是为什么要编写异步代码呢? 4 | 5 | 异步代码允许我们在同一 OS 线程上,同时运行多个任务。在典型的线程应用程序中,如果您想同时下载两个不同的网页,则需要将工作分散到两个不同的线程中,如下所示: 6 | 7 | ```rust 8 | {{#include ../../examples/01_02_why_async/src/lib.rs:get_two_sites}} 9 | ``` 10 | 11 | 这对许多应用程序都很好用 —— 毕竟,线程被设计为可以做到这一点:一次运行多个不同的任务。但是,它们也有一些限制。在不同线程之间切换,以及在线程之间共享数据的过程,涉及很多开销。即使只是一个坐着不执行任何操作的线程,也会占用宝贵的系统资源。这些就是异步代码旨在消除的成本。我们可以使用 Rust `async`/`.await`符号,重写上面的函数,这将使我们能够一次运行多个任务,而无需创建多个线程: 12 | 13 | ```rust 14 | {{#include ../../examples/01_02_why_async/src/lib.rs:get_two_sites_async}} 15 | ``` 16 | 17 | 总体而言,与相应的线程实现相比,异步应用程序可能具有更快的速度和更少的资源。但是,这是有成本的。线程受操作系统的原生支持,并且使用它们不需要任何特殊的编程模型 —— 任何函数都可以创建一个线程,且调用那些使用线程的函数,通常与调用任何普通函数一样容易。但是,异步功能需要语言或库的特殊支持。在 Rust 中,`async fn`创建一个异步函数,该函数返回一个`Future`。要执行该函数的主体(比如:网络 API 的数据),返回的`Future`,必须运行(实际请求)到完成。 18 | 19 | 重要的是要记住,传统的线程应用程序可以非常高效,并且 Rust 的内存占用量小和可预测性,意味着您无需使用任何`async`就能取得成功。异步编程模型的增加的复杂性,并不总是值得的,并且重要的是,考虑使用更简单的线程模型,是否可以为您的应用程序,更好地提供服务。 20 | -------------------------------------------------------------------------------- /src/01_getting_started/03_state_of_async_rust.md: -------------------------------------------------------------------------------- 1 | # The State of Asynchronous Rust 2 | 3 | The asynchronous Rust ecosystem has undergone a lot of evolution over time, 4 | so it can be hard to know what tools to use, what libraries to invest in, 5 | or what documentation to read. However, the `Future` trait inside the standard 6 | library and the `async`/`await` language feature has recently been stabilized. 7 | The ecosystem as a whole is therefore in the midst of migrating 8 | to the newly-stabilized API, after which point churn will be significantly 9 | reduced. 10 | 11 | At the moment, however, the ecosystem is still undergoing rapid development 12 | and the asynchronous Rust experience is unpolished. Most libraries still 13 | use the 0.1 definitions of the `futures` crate, meaning that to interoperate 14 | developers frequently need to reach for the `compat` functionality from the 15 | 0.3 `futures` crate. The `async`/`await` language feature is still new. 16 | Important extensions like `async fn` syntax in trait methods are still 17 | unimplemented, and the current compiler error messages can be difficult to 18 | parse. 19 | 20 | That said, Rust is well on its way to having some of the most performant 21 | and ergonomic support for asynchronous programming around, and if you're not 22 | afraid of doing some spelunking, enjoy your dive into the world of 23 | asynchronous programming in Rust! 24 | 25 | -------------------------------------------------------------------------------- /src/01_getting_started/03_state_of_async_rust.zh.md: -------------------------------------------------------------------------------- 1 | # The State of Asynchronous Rust 2 | 3 | 随着时间的流逝,异步 Rust 生态系统经历了许多演变,因此可能很难知道要使用哪些工具,要投资哪些库或要阅读哪些文档。然而,标准库中的`Future` trait ,和`async`/`await`语言功能最近已稳定。因此,整个生态系统正处于向新稳定的 API 迁移的阶段,此后,用户流失率将大大降低。 4 | 5 | 然而,目前,生态系统仍在快速发展,异步 Rust 体验还不够完善。大多数库仍使用 0.1 版本的`futures`箱子,这意味着,对交互的开发人员来说,经常需要做好 0.3 `futures`箱子兼容的功能。`async`/`await`语言功能仍然是新功能。重要的扩展功能,例如`async fn`语法,在 trait 方法中,仍未实现,并且当前的编译器错误消息可能难以理解。 6 | 7 | 就是说,Rust 正在为异步编程提供一些最高性能和人体工程学的支持,如果您不惧怕进行一些摸索,请尽情享受,进入 Rust 异步编程的世界! 8 | -------------------------------------------------------------------------------- /src/01_getting_started/04_async_await_primer.md: -------------------------------------------------------------------------------- 1 | # `async`/`.await` Primer 2 | 3 | `async`/`.await` is Rust's built-in tool for writing asynchronous functions 4 | that look like synchronous code. `async` transforms a block of code into a 5 | state machine that implements a trait called `Future`. Whereas calling a 6 | blocking function in a synchronous method would block the whole thread, 7 | blocked `Future`s will yield control of the thread, allowing other 8 | `Future`s to run. 9 | 10 | To create an asynchronous function, you can use the `async fn` syntax: 11 | 12 | ```rust 13 | async fn do_something() { ... } 14 | ``` 15 | 16 | The value returned by `async fn` is a `Future`. For anything to happen, 17 | the `Future` needs to be run on an executor. 18 | 19 | ```rust 20 | {{#include ../../examples/01_04_async_await_primer/src/lib.rs:hello_world}} 21 | ``` 22 | 23 | Inside an `async fn`, you can use `.await` to wait for the completion of 24 | another type that implements the `Future` trait, such as the output of 25 | another `async fn`. Unlike `block_on`, `.await` doesn't block the current 26 | thread, but instead asynchronously waits for the future to complete, allowing 27 | other tasks to run if the future is currently unable to make progress. 28 | 29 | For example, imagine that we have three `async fn`: `learn_song`, `sing_song`, 30 | and `dance`: 31 | 32 | ```rust 33 | async fn learn_song() -> Song { ... } 34 | async fn sing_song(song: Song) { ... } 35 | async fn dance() { ... } 36 | ``` 37 | 38 | One way to do learn, sing, and dance would be to block on each of these 39 | individually: 40 | 41 | ```rust 42 | {{#include ../../examples/01_04_async_await_primer/src/lib.rs:block_on_each}} 43 | ``` 44 | 45 | However, we're not giving the best performance possible this way-- we're 46 | only ever doing one thing at once! Clearly we have to learn the song before 47 | we can sing it, but it's possible to dance at the same time as learning and 48 | singing the song. To do this, we can create two separate `async fn` which 49 | can be run concurrently: 50 | 51 | ```rust 52 | {{#include ../../examples/01_04_async_await_primer/src/lib.rs:block_on_main}} 53 | ``` 54 | 55 | In this example, learning the song must happen before singing the song, but 56 | both learning and singing can happen at the same time as dancing. If we used 57 | `block_on(learn_song())` rather than `learn_song().await` in `learn_and_sing`, 58 | the thread wouldn't be able to do anything else while `learn_song` was running. 59 | This would make it impossible to dance at the same time. By `.await`-ing 60 | the `learn_song` future, we allow other tasks to take over the current thread 61 | if `learn_song` is blocked. This makes it possible to run multiple futures 62 | to completion concurrently on the same thread. 63 | 64 | Now that you've learned the basics of `async`/`await`, let's try out an 65 | example. 66 | -------------------------------------------------------------------------------- /src/01_getting_started/04_async_await_primer.zh.md: -------------------------------------------------------------------------------- 1 | # `async`/`.await` Primer 2 | 3 | `async`/`.await`是 Rust 的内置工具,用于编写看起来像同步代码的异步函数。`async`将一个代码区块,转换为实现称为 `Future` trait 的状态机。而在同步方法中,调用阻塞函数将阻塞整个线程,`Future`s 将 yield 对线程的控制权,允许其他`Future`运行。 4 | 5 | 要创建异步功能,您可以使用`async fn`语法: 6 | 7 | ```rust 8 | async fn do_something() { ... } 9 | ``` 10 | 11 | `async fn`传回的值,是一个`Future`。对要发生事情的衡量,`Future`需要在一个 executor 上运行。 12 | 13 | ```rust 14 | {{#include ../../examples/01_04_async_await_primer/src/lib.rs:hello_world}} 15 | ``` 16 | 17 | 在`async fn`内部, 您可以使用`.await`,等待另一种实现`Future` trait 的类型完成它的操作,例如另一个`async fn`的输出。不像`block_on`,`.await`不会阻塞当前线程,而是异步等待 future 完成。这样的话,如果 future 当前无法取得进展,则允许其他任务运行。 18 | 19 | 例如,假设我们有三个`async fn`:`learn_song`,`sing_song`和`dance`: 20 | 21 | ```rust 22 | async fn learn_song() -> Song { ... } 23 | async fn sing_song(song: Song) { ... } 24 | async fn dance() { ... } 25 | ``` 26 | 27 | 选择学习(learn_song),唱歌(sing_song)和跳舞(dance)的任一种,都会分别阻塞每个人: 28 | 29 | ```rust 30 | {{#include ../../examples/01_04_async_await_primer/src/lib.rs:block_on_each}} 31 | ``` 32 | 33 | 但是,我们并没有达到这异步形式的最佳性能 —— 我们一次只能做一件事情!显然,我们必须先学习这首歌,然后才能唱歌,但是可以在学习和唱歌的同时跳舞。为此,我们可以创建两个单独的`async fn`,而它们可以同时运行: 34 | 35 | ```rust 36 | {{#include ../../examples/01_04_async_await_primer/src/lib.rs:block_on_main}} 37 | ``` 38 | 39 | 在此示例中,学习歌曲必须在唱歌之前发生,但是学习和唱歌都可以与跳舞同时发生。如果在`learn_and_sing`,我们使用`block_on(learn_song())`,而不是`learn_song().await`,那只要`learn_song`正在运行,该线程就无法执行其他任何操作。这样就不可能同时跳舞。通过对 `learn_song` future 使用`.await`,那么如果`learn_song`被阻塞,就允许其他任务接管当前线程。这样就可以在同一线程上,同时运行多个 futures。 40 | 41 | 现在,您已经了解了`async`/`await`的基础知识,让我们尝试一个例子。 42 | -------------------------------------------------------------------------------- /src/01_getting_started/05_http_server_example.md: -------------------------------------------------------------------------------- 1 | # Applied: Simple HTTP Server 2 | 3 | Let's use `async`/`.await` to build an echo server! 4 | 5 | To start, run `rustup update stable` to make sure you've got stable Rust 1.39 or newer. Once you've done that, run 6 | `cargo new async-await-echo` to create a new project, and open up 7 | the resulting `async-await-echo` folder. 8 | 9 | Let's add some dependencies to the `Cargo.toml` file: 10 | 11 | ```toml 12 | {{#include ../../examples/01_05_http_server/Cargo.toml:9:18}} 13 | ``` 14 | 15 | Now that we've got our dependencies out of the way, let's start writing some 16 | code. We have some imports to add: 17 | 18 | ```rust 19 | {{#include ../../examples/01_05_http_server/src/lib.rs:imports}} 20 | ``` 21 | 22 | Once the imports are out of the way, we can start putting together the 23 | boilerplate to allow us to serve requests: 24 | 25 | ```rust 26 | {{#include ../../examples/01_05_http_server/src/lib.rs:boilerplate}} 27 | ``` 28 | 29 | If you `cargo run` now, you should see the message "Listening on 30 | http://127.0.0.1:3000" printed on your terminal. If you open that URL in your 31 | browser of choice, you'll see "hello, world!" appear in your browser. 32 | Congratulations! You just wrote your first asynchronous webserver in Rust. 33 | 34 | You can also inspect the request itself, which contains information such as 35 | the request URI, HTTP version, headers, and other metadata. For example, we 36 | can print out the URI of the request like this: 37 | 38 | ```rust 39 | println!("Got request at {:?}", req.uri()); 40 | ``` 41 | 42 | You may have noticed that we're not yet doing 43 | anything asynchronous when handling the request-- we just respond immediately, 44 | so we're not taking advantage of the flexibility that `async fn` gives us. 45 | Rather than just returning a static message, let's try proxying the user's 46 | request to another website using Hyper's HTTP client. 47 | 48 | We start by parsing out the URL we want to request: 49 | 50 | ```rust 51 | {{#include ../../examples/01_05_http_server/src/lib.rs:parse_url}} 52 | ``` 53 | 54 | Then we can create a new `hyper::Client` and use it to make a `GET` request, 55 | returning the response to the user: 56 | 57 | ```rust 58 | {{#include ../../examples/01_05_http_server/src/lib.rs:get_request}} 59 | ``` 60 | 61 | `Client::get` returns a `hyper::client::FutureResponse`, which implements 62 | `Future>` 63 | (or `Future` in futures 0.1 terms). 64 | When we `.await` that future, an HTTP request is sent out, the current task 65 | is suspended, and the task is queued to be continued once a response has 66 | become available. 67 | 68 | Now, if you `cargo run` and open `http://127.0.0.1:3000/foo` in your browser, 69 | you'll see the Rust homepage, and the following terminal output: 70 | 71 | ``` 72 | Listening on http://127.0.0.1:3000 73 | Got request at /foo 74 | making request to http://www.rust-lang.org/en-US/ 75 | request finished-- returning response 76 | ``` 77 | 78 | Congratulations! You just proxied an HTTP request. 79 | -------------------------------------------------------------------------------- /src/01_getting_started/05_http_server_example.zh.md: -------------------------------------------------------------------------------- 1 | # Applied: Simple HTTP Server 2 | 3 | 让我们用`async`/`.await`建立一个回声服务器! 4 | 5 | 开始之前,运行`rustup update stable`,以确保你有 stable Rust 1.39 或更新的版本。一旦完成,就`cargo new async-await-echo`创建新项目,并打开输出的`async-await-echo`文件夹。 6 | 7 | 让我们将一些依赖项,添加到`Cargo.toml`文件: 8 | 9 | ```toml 10 | {{#include ../../examples/01_05_http_server/Cargo.toml:9:18}} 11 | ``` 12 | 13 | 既然我们已经摆脱了依赖关系,让我们开始编写一些代码。我们有一些 imports 要添加: 14 | 15 | ```rust 16 | {{#include ../../examples/01_05_http_server/src/lib.rs:imports}} 17 | ``` 18 | 19 | 一旦搞定这些 imports,我们就可以开始整理样板文件,以便满足以下要求: 20 | 21 | ```rust 22 | {{#include ../../examples/01_05_http_server/src/lib.rs:boilerplate}} 23 | ``` 24 | 25 | 如果你现在`cargo run`,你应该看到信息“Listening on http://127.0.0.1:3000“打印在你的终端上。如果你在你选择的浏览器中,打开这个网址,你会看到“hello, world!”出现在浏览器中。祝贺你!您刚刚在 Rust 中编写了,第一个异步 web 服务器。 26 | 27 | 您还可以检查 request(请求) 本身,它包含诸如,request 的 URI、HTTP 版本、header 和其他元数据等信息。例如,我们可以打印出请求的 URI,如下所示: 28 | 29 | ```rust 30 | println!("Got request at {:?}", req.uri()); 31 | ``` 32 | 33 | 您可能已经注意到,在处理请求时,我们还没有做任何异步操作,我们只是立即响应,所以我们没有利用上`async fn`给我们的灵活性。与其只返回静态消息,不如尝试使用 Hyper 的 HTTP 客户端,将用户的请求代理到另一个网站。 34 | 35 | 我们首先解析出要请求的 URL: 36 | 37 | ```rust 38 | {{#include ../../examples/01_05_http_server/src/lib.rs:parse_url}} 39 | ``` 40 | 41 | 然后,我们可以新建一个新的`hyper::Client`,并使用它,制造一个`GET`请求,将响应返回给用户: 42 | 43 | ```rust 44 | {{#include ../../examples/01_05_http_server/src/lib.rs:get_request}} 45 | ``` 46 | 47 | `Client::get`会返回一个`hyper::client::FutureResponse`,它实现了`Future>`(或`Future`在 futures 0.1 版)。当我们`.await`以后,会发送一个 HTTP 请求,挂起当前任务,并在响应可用时,任务会排队等待继续。 48 | 49 | 现在,如果你现在`cargo run`,在浏览器中打开`http://127.0.0.1:3000/foo`,您将看到 Rust 主页和以下终端输出: 50 | 51 | ``` 52 | Listening on http://127.0.0.1:3000 53 | Got request at /foo 54 | making request to http://www.rust-lang.org/en-US/ 55 | request finished-- returning response 56 | ``` 57 | 58 | 祝贺你!你只是代理了一个 HTTP 请求。 59 | -------------------------------------------------------------------------------- /src/02_execution/01_chapter.md: -------------------------------------------------------------------------------- 1 | # Under the Hood: Executing `Future`s and Tasks 2 | 3 | In this section, we'll cover the underlying structure of how `Future`s and 4 | asynchronous tasks are scheduled. If you're only interested in learning 5 | how to write higher-level code that uses existing `Future` types and aren't 6 | interested in the details of how `Future` types work, you can skip ahead to 7 | the `async`/`await` chapter. However, several of the topics discussed in this 8 | chapter are useful for understanding how `async`/`await` code works, 9 | understanding the runtime and performance properties of `async`/`await` code, 10 | and building new asynchronous primitives. If you decide to skip this section 11 | now, you may want to bookmark it to revisit in the future. 12 | 13 | Now, with that out of the, way, let's talk about the `Future` trait. 14 | -------------------------------------------------------------------------------- /src/02_execution/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # Under the Hood: Executing `Future`s and Tasks 2 | 3 | 在本节中,我们会介绍`Future`和异步是如何调度的。如果您只想学习如何编写使用`Future`类型的高阶代码,并对`Future`类型的工作细节不感兴趣,您可以直接跳到`async`/`await`章节。但是,本章讨论的几个主题如:了解`async`/`await`的工作; `async`/`await`代码的 runtime 和性能属性,构建新的异步原语等,这些对运用异步代码都会有所帮助。如果您决定现在跳过此部分,则可能需要将其添加为书签,以便将来再次访问。 4 | 5 | 现在,让我们来谈谈`Future` trait。 6 | -------------------------------------------------------------------------------- /src/02_execution/02_future.md: -------------------------------------------------------------------------------- 1 | # The `Future` Trait 2 | 3 | The `Future` trait is at the center of asynchronous programming in Rust. 4 | A `Future` is an asynchronous computation that can produce a value 5 | (although that value may be empty, e.g. `()`). A *simplified* version of 6 | the future trait might look something like this: 7 | 8 | ```rust 9 | {{#include ../../examples/02_02_future_trait/src/lib.rs:simple_future}} 10 | ``` 11 | 12 | Futures can be advanced by calling the `poll` function, which will drive the 13 | future as far towards completion as possible. If the future completes, it 14 | returns `Poll::Ready(result)`. If the future is not able to complete yet, it 15 | returns `Poll::Pending` and arranges for the `wake()` function to be called 16 | when the `Future` is ready to make more progress. When `wake()` is called, the 17 | executor driving the `Future` will call `poll` again so that the `Future` can 18 | make more progress. 19 | 20 | Without `wake()`, the executor would have no way of knowing when a particular 21 | future could make progress, and would have to be constantly polling every 22 | future. With `wake()`, the executor knows exactly which futures are ready to 23 | be `poll`ed. 24 | 25 | For example, consider the case where we want to read from a socket that may 26 | or may not have data available already. If there is data, we can read it 27 | in and return `Poll::Ready(data)`, but if no data is ready, our future is 28 | blocked and can no longer make progress. When no data is available, we 29 | must register `wake` to be called when data becomes ready on the socket, 30 | which will tell the executor that our future is ready to make progress. 31 | A simple `SocketRead` future might look something like this: 32 | 33 | ```rust 34 | {{#include ../../examples/02_02_future_trait/src/lib.rs:socket_read}} 35 | ``` 36 | 37 | This model of `Future`s allows for composing together multiple asynchronous 38 | operations without needing intermediate allocations. Running multiple futures 39 | at once or chaining futures together can be implemented via allocation-free 40 | state machines, like this: 41 | 42 | ```rust 43 | {{#include ../../examples/02_02_future_trait/src/lib.rs:join}} 44 | ``` 45 | 46 | This shows how multiple futures can be run simultaneously without needing 47 | separate allocations, allowing for more efficient asynchronous programs. 48 | Similarly, multiple sequential futures can be run one after another, like this: 49 | 50 | ```rust 51 | {{#include ../../examples/02_02_future_trait/src/lib.rs:and_then}} 52 | ``` 53 | 54 | These examples show how the `Future` trait can be used to express asynchronous 55 | control flow without requiring multiple allocated objects and deeply nested 56 | callbacks. With the basic control-flow out of the way, let's talk about the 57 | real `Future` trait and how it is different. 58 | 59 | ```rust 60 | {{#include ../../examples/02_02_future_trait/src/lib.rs:real_future}} 61 | ``` 62 | 63 | The first change you'll notice is that our `self` type is no longer `&mut self`, 64 | but has changed to `Pin<&mut Self>`. We'll talk more about pinning in [a later 65 | section][pinning], but for now know that it allows us to create futures that 66 | are immovable. Immovable objects can store pointers between their fields, 67 | e.g. `struct MyFut { a: i32, ptr_to_a: *const i32 }`. Pinning is necessary 68 | to enable async/await. 69 | 70 | Secondly, `wake: fn()` has changed to `&mut Context<'_>`. In `SimpleFuture`, 71 | we used a call to a function pointer (`fn()`) to tell the future executor that 72 | the future in question should be polled. However, since `fn()` is zero-sized, 73 | it can't store any data about *which* `Future` called `wake`. 74 | 75 | In a real-world scenario, a complex application like a web server may have 76 | thousands of different connections whose wakeups should all be 77 | managed separately. The `Context` type solves this by providing access to 78 | a value of type `Waker`, which can be used to wake up a specific task. 79 | 80 | [pinning]: ../04_pinning/01_chapter.md 81 | -------------------------------------------------------------------------------- /src/02_execution/02_future.zh.md: -------------------------------------------------------------------------------- 1 | # The `Future` Trait 2 | 3 | `Future` trait 是 Rust 异步编程的中心。一个`Future`就是可以产生值的异步计算(尽管该值可能为空,例如`()`)。一种*简化*版本的 Future trait 可能如下所示: 4 | 5 | ```rust 6 | {{#include ../../examples/02_02_future_trait/src/lib.rs:simple_future}} 7 | ``` 8 | 9 | Future 可以通过调用`poll`函数,推动 Future,让其尽可能接近完成。如果 Future 完成,它将返回`Poll::Ready(result)`。如果 Future 还不能完成,它将返回`Poll::Pending`,并安排`wake()`在`Future`准备进一步的时候,调用。当`wake()`调用,executor 驱使 `Future`,再次调用`poll`,这样`Future`就离完成再进一步了。 10 | 11 | 若是没有`wake()`,executor 将无法知道某个特定的 Future 何时可以前进,并且将不得不,不断地 poll 每个 Future 。而有了`wake()`,executor 就能确切知道哪些 Future,已准备`poll`ed。 12 | 13 | 例如,考虑以下情况:我们想从一个 socket(套接字)中,读取数据,而该 socket 的数据不知道有没有。如果**有**数据,我们可以读并返回`Poll::Ready(data)`,但如果没有任何数据 ready,那么我们的 Future 将阻塞,无法再前进。处在没有可用数据时期,当 socket 上的数据 ready 时,我们必须挂上要调用的`wake`,这将告诉 executor,我们的 Future 已准备好前进。 14 | 15 | 一个简单的`SocketRead` Future 可能看起来像这样: 16 | 17 | ```rust 18 | {{#include ../../examples/02_02_future_trait/src/lib.rs:socket_read}} 19 | ``` 20 | 21 | 这个`Future`s 模型可以将多个异步操作组合在一起,而无需中间分配。一次运行多个 Future 或将 Future 链接在一起,可以通过无分配状态机来实现,如下所示: 22 | 23 | ```rust 24 | {{#include ../../examples/02_02_future_trait/src/lib.rs:join}} 25 | ``` 26 | 27 | 这显示了,如何在无需单独分配的情况下,同时运行多个 Future ,从而可以实现更高效的异步程序。同样,可以依次运行多个有序 Future ,如下所示: 28 | 29 | ```rust 30 | {{#include ../../examples/02_02_future_trait/src/lib.rs:and_then}} 31 | ``` 32 | 33 | 这些示例说明了`Future`trait 可用于表示异步控制流,而无需多个分配的对象,和深层嵌套的回调。随着基本控制流程的发展,让我们谈谈真正的`Future` trait 及其不同之处。 34 | 35 | ```rust 36 | {{#include ../../examples/02_02_future_trait/src/lib.rs:real_future}} 37 | ``` 38 | 39 | 您会注意到的第一个变化是`self`类型,不再`&mut self`,而更改为`Pin<&mut Self>`。我们将详细讨论 pinning 在[稍后章节][pinning],但现在知道它使我们能够创建 Immovable(无法移动) 的 Future 。无法移动的对象可以在其字段之间存储指针,例如`struct MyFut { a: i32, ptr_to_a: *const i32 }`。Pinning 是启用 async/await 所必需的。 40 | 41 | 其次,`wake: fn()`已更改为`&mut Context<'_>`。在`SimpleFuture`,我们使用了对函数指针(`fn()`)的一个 call,去告诉 Future 的 executor,应该对有问题的 Future 进行 poll。但是,由于`fn()`大小为零(zero-sized),无法存储有关*哪一个* `Future`调用了`wake`。 42 | 43 | 在现实世界中,像 Web 服务器这样的复杂应用程序,可能具有成千上万个不同的连接,其唤醒都应单独进行管理。`Context` type 通过提供对一个`Waker`类型值的访问,来解决此问题,可用于唤醒特定任务。 44 | 45 | [pinning]: ../04_pinning/01_chapter.zh.md 46 | -------------------------------------------------------------------------------- /src/02_execution/03_wakeups.md: -------------------------------------------------------------------------------- 1 | # Task Wakeups with `Waker` 2 | 3 | It's common that futures aren't able to complete the first time they are 4 | `poll`ed. When this happens, the future needs to ensure that it is polled 5 | again once it is ready to make more progress. This is done with the `Waker` 6 | type. 7 | 8 | Each time a future is polled, it is polled as part of a "task". Tasks are 9 | the top-level futures that have been submitted to an executor. 10 | 11 | `Waker` provides a `wake()` method that can be used to tell the executor that 12 | the associated task should be awoken. When `wake()` is called, the executor 13 | knows that the task associated with the `Waker` is ready to make progress, and 14 | its future should be polled again. 15 | 16 | `Waker` also implements `clone()` so that it can be copied around and stored. 17 | 18 | Let's try implementing a simple timer future using `Waker`. 19 | 20 | ## Applied: Build a Timer 21 | 22 | For the sake of the example, we'll just spin up a new thread when the timer 23 | is created, sleep for the required time, and then signal the timer future 24 | when the time window has elapsed. 25 | 26 | Here are the imports we'll need to get started: 27 | 28 | ```rust 29 | {{#include ../../examples/02_03_timer/src/lib.rs:imports}} 30 | ``` 31 | 32 | Let's start by defining the future type itself. Our future needs a way for the 33 | thread to communicate that the timer has elapsed and the future should complete. 34 | We'll use a shared `Arc>` value to communicate between the thread and 35 | the future. 36 | 37 | ```rust 38 | {{#include ../../examples/02_03_timer/src/lib.rs:timer_decl}} 39 | ``` 40 | 41 | Now, let's actually write the `Future` implementation! 42 | 43 | ```rust 44 | {{#include ../../examples/02_03_timer/src/lib.rs:future_for_timer}} 45 | ``` 46 | 47 | Pretty simple, right? If the thread has set `shared_state.completed = true`, 48 | we're done! Otherwise, we clone the `Waker` for the current task and pass it to 49 | `shared_state.waker` so that the thread can wake the task back up. 50 | 51 | Importantly, we have to update the `Waker` every time the future is polled 52 | because the future may have moved to a different task with a different 53 | `Waker`. This will happen when futures are passed around between tasks after 54 | being polled. 55 | 56 | Finally, we need the API to actually construct the timer and start the thread: 57 | 58 | ```rust 59 | {{#include ../../examples/02_03_timer/src/lib.rs:timer_new}} 60 | ``` 61 | 62 | Woot! That's all we need to build a simple timer future. Now, if only we had 63 | an executor to run the future on... 64 | -------------------------------------------------------------------------------- /src/02_execution/03_wakeups.zh.md: -------------------------------------------------------------------------------- 1 | # Task Wakeups with `Waker` 2 | 3 | Future 一次`poll`ed 就能完成的,并不常见。而多数情况下,Future 需要确保一旦准备好前进,就再次进行轮询(poll) 。而这是通过`Waker`类型,辅助完成的。 4 | 5 | 每次 Future poll 时,都会将其作为“任务(task)”的一部分。任务是已提交给 executor 的顶级 Future 。 6 | 7 | `Waker`提供一个`wake()`方法,它可以用来告诉 executor,应该唤醒的相关**任务**。当`wake()`被调用时, executor 知道与`Waker`相关联的**任务**是准备前进,并且,它的 Future 应再次进行 poll。 8 | 9 | `Waker`还实现了`clone()`,这样就可以将其复制和存储。 10 | 11 | 让我们尝试使用`Waker`,实现一个简单的计时器 future。 12 | 13 | ## Applied: Build a Timer 14 | 15 | 在本示例中,我们将在创建计时器(Timer)时,启动一个新线程,休眠下所需的时间,然后在时间窗口 elapsed(逝去) 后,向计时器发出信号。 16 | 17 | 这是我们需要开始的导入: 18 | 19 | ```rust 20 | {{#include ../../examples/02_03_timer/src/lib.rs:imports}} 21 | ``` 22 | 23 | 让我们从定义 future 类型本身开始。我们的 future 需要一种方法,来让线程可以传达,timer elapsed 和 这个 future 应该完成的信息。我们将使用一个`Arc>`共享值,在线程和 Future 之间进行通信。 24 | 25 | ```rust 26 | {{#include ../../examples/02_03_timer/src/lib.rs:timer_decl}} 27 | ``` 28 | 29 | 现在,让我们实际编写`Future`实现! 30 | 31 | ```rust 32 | {{#include ../../examples/02_03_timer/src/lib.rs:future_for_timer}} 33 | ``` 34 | 35 | 很简单,对吧?如果线程设置了`shared_state.completed = true`,我们就搞定了!不然的话,我们会为当前任务,clone `Waker`,并将其传递给`shared_state.waker`,这样线程才能唤醒备份的任务。 36 | 37 | 重要的是,每次 Future 进行 poll,我们必须更新`Waker`,因为 Future 可能已经转移到,具有一个不同`Waker`的不同任务上了。这种情况在 Future poll 后,在任务之间传来传去时,会发生。 38 | 39 | 最后,我们需要实际构造计时器的 API ,并启动线程: 40 | 41 | ```rust 42 | {{#include ../../examples/02_03_timer/src/lib.rs:timer_new}} 43 | ``` 44 | 45 | Woot!这就是我们构建一个简单的计时器 future 的全部。现在,如果我们只有一个 executor,来运行 future ... 46 | -------------------------------------------------------------------------------- /src/02_execution/04_executor.md: -------------------------------------------------------------------------------- 1 | # Applied: Build an Executor 2 | 3 | Rust's `Future`s are lazy: they won't do anything unless actively driven to 4 | completion. One way to drive a future to completion is to `.await` it inside 5 | an `async` function, but that just pushes the problem one level up: who will 6 | run the futures returned from the top-level `async` functions? The answer is 7 | that we need a `Future` executor. 8 | 9 | `Future` executors take a set of top-level `Future`s and run them to completion 10 | by calling `poll` whenever the `Future` can make progress. Typically, an 11 | executor will `poll` a future once to start off. When `Future`s indicate that 12 | they are ready to make progress by calling `wake()`, they are placed back 13 | onto a queue and `poll` is called again, repeating until the `Future` has 14 | completed. 15 | 16 | In this section, we'll write our own simple executor capable of running a large 17 | number of top-level futures to completion concurrently. 18 | 19 | For this example, we depend on the `futures` crate for the `ArcWake` trait, 20 | which provides an easy way to construct a `Waker`. 21 | 22 | ```toml 23 | [package] 24 | name = "xyz" 25 | version = "0.1.0" 26 | authors = ["XYZ Author"] 27 | edition = "2018" 28 | 29 | [dependencies] 30 | futures-preview = "=0.3.0-alpha.17" 31 | ``` 32 | 33 | Next, we need the following imports at the top of `src/main.rs`: 34 | 35 | ```rust 36 | {{#include ../../examples/02_04_executor/src/lib.rs:imports}} 37 | ``` 38 | 39 | Our executor will work by sending tasks to run over a channel. The executor 40 | will pull events off of the channel and run them. When a task is ready to 41 | do more work (is awoken), it can schedule itself to be polled again by 42 | putting itself back onto the channel. 43 | 44 | In this design, the executor itself just needs the receiving end of the task 45 | channel. The user will get a sending end so that they can spawn new futures. 46 | Tasks themselves are just futures that can reschedule themselves, so we'll 47 | store them as a future paired with a sender that the task can use to requeue 48 | itself. 49 | 50 | ```rust 51 | {{#include ../../examples/02_04_executor/src/lib.rs:executor_decl}} 52 | ``` 53 | 54 | Let's also add a method to spawner to make it easy to spawn new futures. 55 | This method will take a future type, box it and put it in a FutureObj, 56 | and create a new `Arc` with it inside which can be enqueued onto the 57 | executor. 58 | 59 | ```rust 60 | {{#include ../../examples/02_04_executor/src/lib.rs:spawn_fn}} 61 | ``` 62 | 63 | To poll futures, we'll need to create a `Waker`. 64 | As discussed in the [task wakeups section], `Waker`s are responsible 65 | for scheduling a task to be polled again once `wake` is called. Remember that 66 | `Waker`s tell the executor exactly which task has become ready, allowing 67 | them to poll just the futures that are ready to make progress. The easiest way 68 | to create a new `Waker` is by implementing the `ArcWake` trait and then using 69 | the `waker_ref` or `.into_waker()` functions to turn an `Arc` 70 | into a `Waker`. Let's implement `ArcWake` for our tasks to allow them to be 71 | turned into `Waker`s and awoken: 72 | 73 | ```rust 74 | {{#include ../../examples/02_04_executor/src/lib.rs:arcwake_for_task}} 75 | ``` 76 | 77 | When a `Waker` is created from an `Arc`, calling `wake()` on it will 78 | cause a copy of the `Arc` to be sent onto the task channel. Our executor then 79 | needs to pick up the task and poll it. Let's implement that: 80 | 81 | ```rust 82 | {{#include ../../examples/02_04_executor/src/lib.rs:executor_run}} 83 | ``` 84 | 85 | Congratulations! We now have a working futures executor. We can even use it 86 | to run `async/.await` code and custom futures, such as the `TimerFuture` we 87 | wrote earlier: 88 | 89 | ```rust 90 | {{#include ../../examples/02_04_executor/src/lib.rs:main}} 91 | ``` 92 | 93 | [task wakeups section]: ./03_wakeups.md 94 | -------------------------------------------------------------------------------- /src/02_execution/04_executor.zh.md: -------------------------------------------------------------------------------- 1 | # Applied: Build an Executor 2 | 3 | Rust 的`Future`是懒惰的:除非是向着'完成'这一个目标积极前进,否则他们不会做任何事情。向 Future 完成前进的一种方法是,在`async`函数里面,对它`.await`,但这只会将问题升了个级:谁来管理,从顶层 `async`函数返回的 Futures ?答案是:我们需要一个`Future`执行者(executor)。 4 | 5 | `Future` executor 获取一组顶层`Future`,并每当`Future`可以前进时,通过调用`poll`,让它们驶向完成。通常一旦开始,executor 会`poll`一个 Future 。当`Future`表示,因`wake()`的调用准备好前进,会将它们先放回到一个队列,才再次`poll`,重复直到`Future`已经完成。 6 | 7 | 在本节中,我们将编写自己的简单 executor,该 executor 能够让大量顶层 Future 同时驶向完成。 8 | 9 | 在此示例中,我们依赖`futures`箱子,`ArcWake` trait 会用到,它提供了一种轻松的方法来构建`Waker`。 10 | 11 | ```toml 12 | [package] 13 | name = "xyz" 14 | version = "0.1.0" 15 | authors = ["XYZ Author"] 16 | edition = "2018" 17 | 18 | [dependencies] 19 | futures-preview = "=0.3.0-alpha.17" 20 | ``` 21 | 22 | 接下来,我们需要在顶部,添加以下内容`src/main.rs`: 23 | 24 | ```rust 25 | {{#include ../../examples/02_04_executor/src/lib.rs:imports}} 26 | ``` 27 | 28 | 我们的 executor 的工作是,将通过发送任务,在通道上运行。executor 将事件从通道中拉出,并运行它们。当一个任务准备做更多的工作(被唤醒)时,它可以安排自己重新回到通道上,以计划再次进行轮询。 29 | 30 | 在这种设计中,executor 本身仅需要任务通道的接收端。用户将获得发送端,以便他们可以生成新的 Future 。任务本身就是 Future,是可以重新计划自己的。因此,我们会将它们与一个 sender 每每存储在一起,这样,任务就可以用来让自己重新排队。 31 | 32 | ```rust 33 | {{#include ../../examples/02_04_executor/src/lib.rs:executor_decl}} 34 | ``` 35 | 36 | 我们还向 spawner 添加一种方法,以使其易于生成新的 Future 。此方法将拿到一个 Future 类型,将其装箱,并放入 FutureObj 中,然后创建一个新类型`Arc`,它的内部可以在 executor 上,排队。 37 | 38 | ```rust 39 | {{#include ../../examples/02_04_executor/src/lib.rs:spawn_fn}} 40 | ``` 41 | 42 | 要轮询 Future ,我们需要创建一个`Waker`。正如在[唤醒章节][task wakeups section],`Waker`负责安排,一旦`wake`调用了,就再次轮询的任务。记住,`Waker`s 是会告诉 executor,确切的那些任务已经准备就绪,只轮询准备好前进的 Future。创建一个新的`Waker`最简单的方法是,通过实现`ArcWake` trait ,然后使用`waker_ref`要么`.into_waker()`函数,将`Arc`转变成一个`Waker`。让我们,为我们的任务实现`ArcWake`,这样就可以转变为`Waker`,和被唤醒啦: 43 | 44 | ```rust 45 | {{#include ../../examples/02_04_executor/src/lib.rs:arcwake_for_task}} 46 | ``` 47 | 48 | 当一个新建的`Waker`,从`Arc`而来,那么我们在它上面调用`wake()`,将导致`Arc`的一个 copy 发送到任务通道。然后,我们的 executor 需要选择任务,并进行轮询。让我们实现一下: 49 | 50 | ```rust 51 | {{#include ../../examples/02_04_executor/src/lib.rs:executor_run}} 52 | ``` 53 | 54 | 恭喜你!我们现在有一个能工作的 Future executor。我们甚至可以使用它,来运行`async/.await`代码和自定义 Future ,例如,我们之前写过的`TimerFuture`: 55 | 56 | ```rust 57 | {{#include ../../examples/02_04_executor/src/lib.rs:main}} 58 | ``` 59 | 60 | [task wakeups section]: ./03_wakeups.zh.md 61 | -------------------------------------------------------------------------------- /src/02_execution/05_io.md: -------------------------------------------------------------------------------- 1 | # Executors and System IO 2 | 3 | In the previous section on [The `Future` Trait], we discussed this example of 4 | a future that performed an asynchronous read on a socket: 5 | 6 | ```rust 7 | {{#include ../../examples/02_02_future_trait/src/lib.rs:socket_read}} 8 | ``` 9 | 10 | This future will read available data on a socket, and if no data is available, 11 | it will yield to the executor, requesting that its task be awoken when the 12 | socket becomes readable again. However, it's not clear from this example how 13 | the `Socket` type is implemented, and in particular it isn't obvious how the 14 | `set_readable_callback` function works. How can we arrange for `lw.wake()` 15 | to be called once the socket becomes readable? One option would be to have 16 | a thread that continually checks whether `socket` is readable, calling 17 | `wake()` when appropriate. However, this would be quite inefficient, requiring 18 | a separate thread for each blocked IO future. This would greatly reduce the 19 | efficiency of our async code. 20 | 21 | In practice, this problem is solved through integration with an IO-aware 22 | system blocking primitive, such as `epoll` on Linux, `kqueue` on FreeBSD and 23 | Mac OS, IOCP on Windows, and `port`s on Fuchsia (all of which are exposed 24 | through the cross-platform Rust crate [`mio`]). These primitives all allow 25 | a thread to block on multiple asynchronous IO events, returning once one of 26 | the events completes. In practice, these APIs usually look something like 27 | this: 28 | 29 | ```rust 30 | struct IoBlocker { 31 | ... 32 | } 33 | 34 | struct Event { 35 | // An ID uniquely identifying the event that occurred and was listened for. 36 | id: usize, 37 | 38 | // A set of signals to wait for, or which occurred. 39 | signals: Signals, 40 | } 41 | 42 | impl IoBlocker { 43 | /// Create a new collection of asynchronous IO events to block on. 44 | fn new() -> Self { ... } 45 | 46 | /// Express an interest in a particular IO event. 47 | fn add_io_event_interest( 48 | &self, 49 | 50 | /// The object on which the event will occur 51 | io_object: &IoObject, 52 | 53 | /// A set of signals that may appear on the `io_object` for 54 | /// which an event should be triggered, paired with 55 | /// an ID to give to events that result from this interest. 56 | event: Event, 57 | ) { ... } 58 | 59 | /// Block until one of the events occurs. 60 | fn block(&self) -> Event { ... } 61 | } 62 | 63 | let mut io_blocker = IoBlocker::new(); 64 | io_blocker.add_io_event_interest( 65 | &socket_1, 66 | Event { id: 1, signals: READABLE }, 67 | ); 68 | io_blocker.add_io_event_interest( 69 | &socket_2, 70 | Event { id: 2, signals: READABLE | WRITABLE }, 71 | ); 72 | let event = io_blocker.block(); 73 | 74 | // prints e.g. "Socket 1 is now READABLE" if socket one became readable. 75 | println!("Socket {:?} is now {:?}", event.id, event.signals); 76 | ``` 77 | 78 | Futures executors can use these primitives to provide asynchronous IO objects 79 | such as sockets that can configure callbacks to be run when a particular IO 80 | event occurs. In the case of our `SocketRead` example above, the 81 | `Socket::set_readable_callback` function might look like the following pseudocode: 82 | 83 | ```rust 84 | impl Socket { 85 | fn set_readable_callback(&self, waker: Waker) { 86 | // `local_executor` is a reference to the local executor. 87 | // this could be provided at creation of the socket, but in practice 88 | // many executor implementations pass it down through thread local 89 | // storage for convenience. 90 | let local_executor = self.local_executor; 91 | 92 | // Unique ID for this IO object. 93 | let id = self.id; 94 | 95 | // Store the local waker in the executor's map so that it can be called 96 | // once the IO event arrives. 97 | local_executor.event_map.insert(id, waker); 98 | local_executor.add_io_event_interest( 99 | &self.socket_file_descriptor, 100 | Event { id, signals: READABLE }, 101 | ); 102 | } 103 | } 104 | ``` 105 | 106 | We can now have just one executor thread which can receive and dispatch any 107 | IO event to the appropriate `Waker`, which will wake up the corresponding 108 | task, allowing the executor to drive more tasks to completion before returning 109 | to check for more IO events (and the cycle continues...). 110 | 111 | [The `Future` Trait]: ./02_future.md 112 | [`mio`]: https://github.com/tokio-rs/mio 113 | -------------------------------------------------------------------------------- /src/02_execution/05_io.zh.md: -------------------------------------------------------------------------------- 1 | # Executors and System IO 2 | 3 | 在[The `Future` Trait]的上一章节中,我们讨论了这个 Future 在套接字上,执行异步读取的示例: 4 | 5 | ```rust 6 | {{#include ../../examples/02_02_future_trait/src/lib.rs:socket_read}} 7 | ``` 8 | 9 | 这个 Future 将读取套接字上的可用数据,如果没有可用数据,它将交还给 executor,要求在套接字再次变得可读时,唤醒这个任务。但是,根据此示例尚不清楚,这个`Socket`类型是怎么实现的,尤其是`set_readable_callback`函数是如何工作的。我们如何安排`lw.wake()`,在一旦套接字变得可读时,就被调用?一种选择是,让一个线程不断检查`socket`是否可读,在适当的时候调用`wake()`。但是,这将是非常低效的,需要为每个阻塞的 IO Future 使用一个单独的线程。这将大大降低我们异步代码的效率。 10 | 11 | 实际上,此问题是通过与 IO-感知系统阻塞原语交互来解决。例如,`epoll`在 Linux 上,`kqueue`在 FreeBSD 和 Mac OS 上,在 Windows 上为 IOCP,以及 Fuchsia 的`port`(所有这些都通过跨平台的 Rust 箱子[`mio`]揭露)。这些原语都允许一个线程,在多个异步 IO 事件上阻塞,并在事件的其中一个完成后返回。实际上,这些 API 通常如下所示: 12 | 13 | ```rust 14 | struct IoBlocker { 15 | ... 16 | } 17 | 18 | struct Event { 19 | // An ID uniquely identifying the event that occurred and was listened for. 20 | id: usize, 21 | 22 | // A set of signals to wait for, or which occurred. 23 | signals: Signals, 24 | } 25 | 26 | impl IoBlocker { 27 | /// Create a new collection of asynchronous IO events to block on. 28 | fn new() -> Self { ... } 29 | 30 | /// Express an interest in a particular IO event. 31 | fn add_io_event_interest( 32 | &self, 33 | 34 | /// The object on which the event will occur 35 | io_object: &IoObject, 36 | 37 | /// A set of signals that may appear on the `io_object` for 38 | /// which an event should be triggered, paired with 39 | /// an ID to give to events that result from this interest. 40 | event: Event, 41 | ) { ... } 42 | 43 | /// Block until one of the events occurs. 44 | fn block(&self) -> Event { ... } 45 | } 46 | 47 | let mut io_blocker = IoBlocker::new(); 48 | io_blocker.add_io_event_interest( 49 | &socket_1, 50 | Event { id: 1, signals: READABLE }, 51 | ); 52 | io_blocker.add_io_event_interest( 53 | &socket_2, 54 | Event { id: 2, signals: READABLE | WRITABLE }, 55 | ); 56 | let event = io_blocker.block(); 57 | 58 | // prints e.g. "Socket 1 is now READABLE" if socket one became readable. 59 | println!("Socket {:?} is now {:?}", event.id, event.signals); 60 | ``` 61 | 62 | Future executor 可以使用这些原语来提供异步 IO 对象(例如套接字),这些对象可以配置,在发生特定 IO 事件时,运行的回调。在我们上面例子的`SocketRead`情况下`Socket::set_readable_callback`函数可能类似于以下伪代码: 63 | 64 | ```rust 65 | impl Socket { 66 | fn set_readable_callback(&self, waker: Waker) { 67 | // `local_executor` is a reference to the local executor. 68 | // this could be provided at creation of the socket, but in practice 69 | // many executor implementations pass it down through thread local 70 | // storage for convenience. 71 | let local_executor = self.local_executor; 72 | 73 | // Unique ID for this IO object. 74 | let id = self.id; 75 | 76 | // Store the local waker in the executor's map so that it can be called 77 | // once the IO event arrives. 78 | local_executor.event_map.insert(id, waker); 79 | local_executor.add_io_event_interest( 80 | &self.socket_file_descriptor, 81 | Event { id, signals: READABLE }, 82 | ); 83 | } 84 | } 85 | ``` 86 | 87 | 现在,我们只有一个 executor 线程,该线程可以接收任何 IO 事件,并将 IO 事件分配给相应的`Waker`,这将唤醒相应的任务,从而使 executor 在返回以检查更多 IO 事件之前,可以驱使更多任务驶向完成,(且该循环会继续...)。 88 | 89 | [the `future` trait]: ./02_future.zh.md 90 | [`mio`]: https://github.com/tokio-rs/mio 91 | -------------------------------------------------------------------------------- /src/03_async_await/01_chapter.md: -------------------------------------------------------------------------------- 1 | # `async`/`.await` 2 | 3 | In [the first chapter], we took a brief look at `async`/`.await` and used 4 | it to build a simple server. This chapter will discuss `async`/`.await` in 5 | greater detail, explaining how it works and how `async` code differs from 6 | traditional Rust programs. 7 | 8 | `async`/`.await` are special pieces of Rust syntax that make it possible to 9 | yield control of the current thread rather than blocking, allowing other 10 | code to make progress while waiting on an operation to complete. 11 | 12 | There are two main ways to use `async`: `async fn` and `async` blocks. 13 | Each returns a value that implements the `Future` trait: 14 | 15 | ```rust 16 | {{#include ../../examples/03_01_async_await/src/lib.rs:async_fn_and_block_examples}} 17 | ``` 18 | 19 | As we saw in the first chapter, `async` bodies and other futures are lazy: 20 | they do nothing until they are run. The most common way to run a `Future` 21 | is to `.await` it. When `.await` is called on a `Future`, it will attempt 22 | to run it to completion. If the `Future` is blocked, it will yield control 23 | of the current thread. When more progress can be made, the `Future` will be picked 24 | up by the executor and will resume running, allowing the `.await` to resolve. 25 | 26 | ## `async` Lifetimes 27 | 28 | Unlike traditional functions, `async fn`s which take references or other 29 | non-`'static` arguments return a `Future` which is bounded by the lifetime of 30 | the arguments: 31 | 32 | ```rust 33 | {{#include ../../examples/03_01_async_await/src/lib.rs:lifetimes_expanded}} 34 | ``` 35 | 36 | This means that the future returned from an `async fn` must be `.await`ed 37 | while its non-`'static` arguments are still valid. In the common 38 | case of `.await`ing the future immediately after calling the function 39 | (as in `foo(&x).await`) this is not an issue. However, if storing the future 40 | or sending it over to another task or thread, this may be an issue. 41 | 42 | One common workaround for turning an `async fn` with references-as-arguments 43 | into a `'static` future is to bundle the arguments with the call to the 44 | `async fn` inside an `async` block: 45 | 46 | ```rust 47 | {{#include ../../examples/03_01_async_await/src/lib.rs:static_future_with_borrow}} 48 | ``` 49 | 50 | By moving the argument into the `async` block, we extend its lifetime to match 51 | that of the `Future` returned from the call to `good`. 52 | 53 | ## `async move` 54 | 55 | `async` blocks and closures allow the `move` keyword, much like normal 56 | closures. An `async move` block will take ownership of the variables it 57 | references, allowing it to outlive the current scope, but giving up the ability 58 | to share those variables with other code: 59 | 60 | ```rust 61 | {{#include ../../examples/03_01_async_await/src/lib.rs:async_move_examples}} 62 | ``` 63 | 64 | ## `.await`ing on a Multithreaded Executor 65 | 66 | Note that, when using a multithreaded `Future` executor, a `Future` may move 67 | between threads, so any variables used in `async` bodies must be able to travel 68 | between threads, as any `.await` can potentially result in a switch to a new 69 | thread. 70 | 71 | This means that it is not safe to use `Rc`, `&RefCell` or any other types 72 | that don't implement the `Send` trait, including references to types that don't 73 | implement the `Sync` trait. 74 | 75 | (Caveat: it is possible to use these types so long as they aren't in scope 76 | during a call to `.await`.) 77 | 78 | Similarly, it isn't a good idea to hold a traditional non-futures-aware lock 79 | across an `.await`, as it can cause the threadpool to lock up: one task could 80 | take out a lock, `.await` and yield to the executor, allowing another task to 81 | attempt to take the lock and cause a deadlock. To avoid this, use the `Mutex` 82 | in `futures::lock` rather than the one from `std::sync`. 83 | 84 | [the first chapter]: ../01_getting_started/04_async_await_primer.md 85 | -------------------------------------------------------------------------------- /src/03_async_await/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # `async`/`.await` 2 | 3 | 在[第一章节][the first chapter],我们简要介绍了`async`/`.await`,并用它来构建一个简单的服务器。本章将更为详细讨论`async`/`.await`的它如何工作以及如何`async`代码与传统的 Rust 程序不同。 4 | 5 | `async`/`.await`是 Rust 语法的特殊部分,它使得可以 yield 对当前线程的控制而不是阻塞,从而允许在等待操作完成时,其他代码可以运行。 6 | 7 | `async`有两种主要的使用方式:`async fn`和`async`代码块。每个返回一个实现`Future`trait: 8 | 9 | ```rust 10 | {{#include ../../examples/03_01_async_await/src/lib.rs:async_fn_and_block_examples}} 11 | ``` 12 | 13 | 正如我们在第一章所看到的,`async`主体和其他 Future 是懒惰的:它们在运行之前什么也不做。最常见的,运行一个`Future`的方式是`.await`它。当在`Future`上调用`.await`的时候,它将尝试运行它,以完成操作。如果`Future`阻塞,它将 yield(归还)当前线程的控制。当 Future 可以更进一步时,`Future`将由 executor 接管并恢复运行,允许`.await`搞定这个 future。 14 | 15 | ## `async` Lifetimes 16 | 17 | 与传统函数不同,`async fn`会接受一个引用,或其他非`'static`的参数,并返回一个`Future`,这受到这些个参数生命周期的限制: 18 | 19 | ```rust 20 | {{#include ../../examples/03_01_async_await/src/lib.rs:lifetimes_expanded}} 21 | ``` 22 | 23 | 这意味着,来自一个`async fn`的 Future 必须被`.await`ed,期间它的非`'static`参数仍然有效。在通常情况下,在调用`.await`之后,会立即执行 (如`foo(&x).await`),而这并不是问题。但是,如果存储这个 Future,或将其发送到另一个任务或线程,则可能会出现问题。 24 | 25 | 一种常见的变通方法是,将引用作为参数的`async fn`函数转换成一个`'static` Future,具体是在一个`async`代码块里面,将这个参数与这`async fn`的调用捆绑在一起: 26 | 27 | ```rust 28 | {{#include ../../examples/03_01_async_await/src/lib.rs:static_future_with_borrow}} 29 | ``` 30 | 31 | 通过将参数移到`async`代码块,我们延长了其生命周期,以匹配,来自`good`的调用返回的`Future`。 32 | 33 | ## `async move` 34 | 35 | `async`代码块和闭包允许`move`关键字,很像普通的闭包。一个`async move`代码块将拥有,对其引用的变量的所有权,从而使生命周期超过当前范围,但放弃了与其他代码共享这些变量的能力: 36 | 37 | ```rust 38 | {{#include ../../examples/03_01_async_await/src/lib.rs:async_move_examples}} 39 | ``` 40 | 41 | ## `.await`ing on a Multithreaded Executor 42 | 43 | 请注意,使用一个多线程的`Future` executor,一个`Future`可能会在线程之间移动,因此在`async`主体内的任何使用变量,必须能够在线程之间移动,正如任一`.await`都具有在一个 switch,就去到新线程的潜在结果。 44 | 45 | 这意味着,使用`Rc`,`&RefCell`或任何其他未实现`Send` trait,包括那些未实现`Sync`trait 的类型的引用都是不安全。 46 | 47 | (注意:在调用`.await`期间,只要它们不在范围内,就有可能使用这些类型) 48 | 49 | 同样,想在一个`.await`上,搞个传统的 non-futures-aware 锁,也不是一个好主意,因为它可能导致线程池锁定:一项任务可以拿一个锁,之后`.await`并 yield 到 executor,而再允许另一个任务尝试获取该锁,也就会导致死锁。为避免这种情况,请在`futures::lock`使用`Mutex`,而不是`std::sync`里的那个。 50 | 51 | [the first chapter]: ../01_getting_started/04_async_await_primer.zh.md 52 | -------------------------------------------------------------------------------- /src/04_pinning/01_chapter.md: -------------------------------------------------------------------------------- 1 | # Pinning 2 | 3 | To poll futures, they must be pinned using a special type called 4 | `Pin`. If you read the explanation of [the `Future` trait] in the 5 | previous section ["Executing `Future`s and Tasks"], you'll recognise 6 | `Pin` from the `self: Pin<&mut Self>` in the `Future:poll` method's definition. 7 | But what does it mean, and why do we need it? 8 | 9 | ## Why Pinning 10 | 11 | Pinning makes it possible to guarantee that an object won't ever be moved. 12 | To understand why this is necessary, we need to remember how `async`/`.await` 13 | works. Consider the following code: 14 | 15 | ```rust 16 | let fut_one = ...; 17 | let fut_two = ...; 18 | async move { 19 | fut_one.await; 20 | fut_two.await; 21 | } 22 | ``` 23 | 24 | Under the hood, this creates an anonymous type that implements `Future`, 25 | providing a `poll` method that looks something like this: 26 | 27 | ```rust 28 | // The `Future` type generated by our `async { ... }` block 29 | struct AsyncFuture { 30 | fut_one: FutOne, 31 | fut_two: FutTwo, 32 | state: State, 33 | } 34 | 35 | // List of states our `async` block can be in 36 | enum State { 37 | AwaitingFutOne, 38 | AwaitingFutTwo, 39 | Done, 40 | } 41 | 42 | impl Future for AsyncFuture { 43 | type Output = (); 44 | 45 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { 46 | loop { 47 | match self.state { 48 | State::AwaitingFutOne => match self.fut_one.poll(..) { 49 | Poll::Ready(()) => self.state = State::AwaitingFutTwo, 50 | Poll::Pending => return Poll::Pending, 51 | } 52 | State::AwaitingFutTwo => match self.fut_two.poll(..) { 53 | Poll::Ready(()) => self.state = State::Done, 54 | Poll::Pending => return Poll::Pending, 55 | } 56 | State::Done => return Poll::Ready(()), 57 | } 58 | } 59 | } 60 | } 61 | ``` 62 | 63 | 64 | When `poll` is first called, it will poll `fut_one`. If `fut_one` can't 65 | complete, `AsyncFuture::poll` will return. Future calls to `poll` will pick 66 | up where the previous one left off. This process continues until the future 67 | is able to successfully complete. 68 | 69 | However, what happens if we have an `async` block that uses references? 70 | For example: 71 | 72 | ```rust 73 | async { 74 | let mut x = [0; 128]; 75 | let read_into_buf_fut = read_into_buf(&mut x); 76 | read_into_buf_fut.await; 77 | println!("{:?}", x); 78 | } 79 | ``` 80 | 81 | What struct does this compile down to? 82 | 83 | ```rust 84 | struct ReadIntoBuf<'a> { 85 | buf: &'a mut [u8], // points to `x` below 86 | } 87 | 88 | struct AsyncFuture { 89 | x: [u8; 128], 90 | read_into_buf_fut: ReadIntoBuf<'what_lifetime?>, 91 | } 92 | ``` 93 | 94 | Here, the `ReadIntoBuf` future holds a reference into the other field of our 95 | structure, `x`. However, if `AsyncFuture` is moved, the location of `x` will 96 | move as well, invalidating the pointer stored in `read_into_buf_fut.buf`. 97 | 98 | Pinning futures to a particular spot in memory prevents this problem, making 99 | it safe to create references to values inside an `async` block. 100 | 101 | ## How to Use Pinning 102 | 103 | The `Pin` type wraps pointer types, guaranteeing that the values behind the 104 | pointer won't be moved. For example, `Pin<&mut T>`, `Pin<&T>`, 105 | `Pin>` all guarantee that `T` won't be moved. 106 | 107 | Most types don't have a problem being moved. These types implement a trait 108 | called `Unpin`. Pointers to `Unpin` types can be freely placed into or taken 109 | out of `Pin`. For example, `u8` is `Unpin`, so `Pin<&mut u8>` behaves just like 110 | a normal `&mut u8`. 111 | 112 | Some functions require the futures they work with to be `Unpin`. To use a 113 | `Future` or `Stream` that isn't `Unpin` with a function that requires 114 | `Unpin` types, you'll first have to pin the value using either 115 | `Box::pin` (to create a `Pin>`) or the `pin_utils::pin_mut!` macro 116 | (to create a `Pin<&mut T>`). `Pin>` and `Pin<&mut Fut>` can both be 117 | used as futures, and both implement `Unpin`. 118 | 119 | For example: 120 | 121 | ```rust 122 | use pin_utils::pin_mut; // `pin_utils` is a handy crate available on crates.io 123 | 124 | // A function which takes a `Future` that implements `Unpin`. 125 | fn execute_unpin_future(x: impl Future + Unpin) { ... } 126 | 127 | let fut = async { ... }; 128 | execute_unpin_future(fut); // Error: `fut` does not implement `Unpin` trait 129 | 130 | // Pinning with `Box`: 131 | let fut = async { ... }; 132 | let fut = Box::pin(fut); 133 | execute_unpin_future(fut); // OK 134 | 135 | // Pinning with `pin_mut!`: 136 | let fut = async { ... }; 137 | pin_mut!(fut); 138 | execute_unpin_future(fut); // OK 139 | ``` 140 | 141 | ["Executing `Future`s and Tasks"]: ../02_execution/01_chapter.md 142 | [the `Future` trait]: ../02_execution/02_future.md 143 | -------------------------------------------------------------------------------- /src/04_pinning/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # Pinning 2 | 3 | 要轮询 Future ,必须使用一种称为`Pin`的特殊类型,来固定 Future。如果您阅读了在上一节["Executing `Future`s and Tasks"]中,[the `Future` trait]的解释,您会发现`Pin`来自`self: Pin<&mut Self>`,它是`Future:poll`方法的定义。但这究竟是什么意思,为什么我们需要它? 4 | 5 | ## Why Pinning 6 | 7 | 固定(Pinning)能得到一个保证,就是确保永远不会移动对象。要了解这样是必要的,我们需要记起`async`/`.await`的工作原理。考虑以下代码: 8 | 9 | ```rust 10 | let fut_one = ...; 11 | let fut_two = ...; 12 | async move { 13 | fut_one.await; 14 | fut_two.await; 15 | } 16 | ``` 17 | 18 | 在幕后,新建一个实现`Future`的匿名类型,它提供一个`poll`(轮询)方法,看起来像这样的: 19 | 20 | ```rust 21 | // 这个 `Future` 类型,由我们的 `async { ... }` 代码块生成而来 22 | struct AsyncFuture { 23 | fut_one: FutOne, 24 | fut_two: FutTwo, 25 | state: State, 26 | } 27 | 28 | // 是我们 `async` 代码块可处于的,状态列表 29 | enum State { 30 | AwaitingFutOne, 31 | AwaitingFutTwo, 32 | Done, 33 | } 34 | 35 | impl Future for AsyncFuture { 36 | type Output = (); 37 | 38 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { 39 | loop { 40 | match self.state { 41 | State::AwaitingFutOne => match self.fut_one.poll(..) { 42 | Poll::Ready(()) => self.state = State::AwaitingFutTwo, 43 | Poll::Pending => return Poll::Pending, 44 | } 45 | State::AwaitingFutTwo => match self.fut_two.poll(..) { 46 | Poll::Ready(()) => self.state = State::Done, 47 | Poll::Pending => return Poll::Pending, 48 | } 49 | State::Done => return Poll::Ready(()), 50 | } 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | 当`poll`先被调用,它将轮询`fut_one`。如果`fut_one`还未完成,`AsyncFuture::poll`将返回。 Future 对`poll`进行调用,将在上一个停止的地方继续。这个过程一直持续到 Future 能成功完成。 57 | 58 | 但是,如果我们有一个,使用引用的`async`代码块?例如: 59 | 60 | ```rust 61 | async { 62 | let mut x = [0; 128]; 63 | let read_into_buf_fut = read_into_buf(&mut x); // &mut x 64 | read_into_buf_fut.await; 65 | println!("{:?}", x); 66 | } 67 | ``` 68 | 69 | 这会编译成什么结构? 70 | 71 | ```rust 72 | struct ReadIntoBuf<'a> { 73 | buf: &'a mut [u8], // 指向下面的 `x` 74 | } 75 | 76 | struct AsyncFuture { 77 | x: [u8; 128], 78 | read_into_buf_fut: ReadIntoBuf<'what_lifetime?>, 79 | } 80 | ``` 81 | 82 | 在这里,`ReadIntoBuf` Future 拿着一个引用,指向我们结构的其他字段,即`x`。但是,如果`AsyncFuture`被移动(move),`x`的位置也会移动,使存储在`read_into_buf_fut.buf`中的指针无效。 83 | 84 | 将 Future 固定到内存中的特定位置,可以避免此问题,从而可以安全地创建,对`async`代码块内部值的引用。 85 | 86 | ## How to Use Pinning 87 | 88 | `Pin`类型会包裹着指针类型,保证指针后面的值不会移动。例如,`Pin<&mut T>`,`Pin<&T>`,`Pin>`,所有的这些,都保证`T`不会移动。 89 | 90 | 大多数类型在移动时,都没有问题。这些类型实现了一种称为`Unpin`的 trait。`Unpin`类型指针可以与`Pin`自由放入或取出。例如,`u8`是`Unpin`,所以`Pin<&mut u8>`表现就像正常`&mut u8`。 91 | 92 | 某些函数需要,要求与之配合使用的 Future 是`Unpin`。要使用不是`Unpin`的`Future`要么`Stream`,配合那些那需要`Unpin`类型的函数,那您首先必须 pin the value,方法有两种:`Box::pin`(创建一个`Pin>`) 或者 `pin_utils::pin_mut!`宏(创建一个`Pin<&mut T>`)。`Pin>`和`Pin<&mut Fut>`既可以用作 Future ,也实现了`Unpin`。 93 | 94 | 例如: 95 | 96 | ```rust 97 | use pin_utils::pin_mut; // `pin_utils` is a handy crate available on crates.io 98 | 99 | // A function which takes a `Future` that implements `Unpin`. 100 | fn execute_unpin_future(x: impl Future + Unpin) { ... } 101 | 102 | let fut = async { ... }; 103 | execute_unpin_future(fut); // Error: `fut` does not implement `Unpin` trait 104 | 105 | // Pinning with `Box`: 106 | let fut = async { ... }; 107 | let fut = Box::pin(fut); 108 | execute_unpin_future(fut); // OK 109 | 110 | // Pinning with `pin_mut!`: 111 | let fut = async { ... }; 112 | pin_mut!(fut); 113 | execute_unpin_future(fut); // OK 114 | ``` 115 | 116 | ["executing `future`s and tasks"]: ../02_execution/01_chapter.zh.md 117 | [the `future` trait]: ../02_execution/02_future.zh.md 118 | -------------------------------------------------------------------------------- /src/05_streams/01_chapter.md: -------------------------------------------------------------------------------- 1 | # The `Stream` Trait 2 | 3 | The `Stream` trait is similar to `Future` but can yield multiple values before 4 | completing, similar to the `Iterator` trait from the standard library: 5 | 6 | ```rust 7 | {{#include ../../examples/05_01_streams/src/lib.rs:stream_trait}} 8 | ``` 9 | 10 | One common example of a `Stream` is the `Receiver` for the channel type from 11 | the `futures` crate. It will yield `Some(val)` every time a value is sent 12 | from the `Sender` end, and will yield `None` once the `Sender` has been 13 | dropped and all pending messages have been received: 14 | 15 | ```rust 16 | {{#include ../../examples/05_01_streams/src/lib.rs:channels}} 17 | ``` 18 | -------------------------------------------------------------------------------- /src/05_streams/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # The `Stream` Trait 2 | 3 | `Stream` trait 类似于`Future`,但可以在完成之前,yield 多个值,类似于标准库的 `Iterator` trait: 4 | 5 | ```rust 6 | {{#include ../../examples/05_01_streams/src/lib.rs:stream_trait}} 7 | ``` 8 | 9 | 一个`Stream`的常见例子是,这个`Receiver`用于`futures`箱子的 channel 类型。它会在每次`Sender`端发送一个值,都会 yield `Some(val)`,并且一旦`Sender`被 dropped 和接收到了所有 pending 消息,就会 yield `None`: 10 | 11 | ```rust 12 | {{#include ../../examples/05_01_streams/src/lib.rs:channels}} 13 | ``` 14 | -------------------------------------------------------------------------------- /src/05_streams/02_iteration_and_concurrency.md: -------------------------------------------------------------------------------- 1 | # Iteration and Concurrency 2 | 3 | Similar to synchronous `Iterator`s, there are many different ways to iterate 4 | over and process the values in a `Stream`. There are combinator-style methods 5 | such as `map`, `filter`, and `fold`, and their early-exit-on-error cousins 6 | `try_map`, `try_filter`, and `try_fold`. 7 | 8 | Unfortunately, `for` loops are not usable with `Stream`s, but for 9 | imperative-style code, `while let` and the `next`/`try_next` functions can 10 | be used: 11 | 12 | ```rust 13 | {{#include ../../examples/05_02_iteration_and_concurrency/src/lib.rs:nexts}} 14 | ``` 15 | 16 | However, if we're just processing one element at a time, we're potentially 17 | leaving behind opportunity for concurrency, which is, after all, why we're 18 | writing async code in the first place. To process multiple items from a stream 19 | concurrently, use the `for_each_concurrent` and `try_for_each_concurrent` 20 | methods: 21 | 22 | ```rust 23 | {{#include ../../examples/05_02_iteration_and_concurrency/src/lib.rs:try_for_each_concurrent}} 24 | ``` 25 | -------------------------------------------------------------------------------- /src/05_streams/02_iteration_and_concurrency.zh.md: -------------------------------------------------------------------------------- 1 | # Iteration and Concurrency 2 | 3 | 类似于同步  方式的`Iterator`,这里有很多不同的方法可以迭代和处理一个`Stream`中的值。有组合器样式的方法,例如`map`,`filter`和`fold`和他们的有错误就早退的表弟`try_map`,`try_filter`和`try_fold`。 4 | 5 | 不幸,`for`循环不适用于`Stream`s,但对于命令式代码,`while let`和`next`/`try_next`函数可以这样用: 6 | 7 | ```rust 8 | {{#include ../../examples/05_02_iteration_and_concurrency/src/lib.rs:nexts}} 9 | ``` 10 | 11 | 但是,如果我们一次只处理一个元素,则可能会失去了并发的机会,这毕竟这是我们要编写异步代码的首要原因。要同时处理一个 stream 中的多个 items,请使用`for_each_concurrent`和`try_for_each_concurrent`方法: 12 | 13 | ```rust 14 | {{#include ../../examples/05_02_iteration_and_concurrency/src/lib.rs:try_for_each_concurrent}} 15 | ``` 16 | -------------------------------------------------------------------------------- /src/06_multiple_futures/01_chapter.md: -------------------------------------------------------------------------------- 1 | # Executing Multiple Futures at a Time 2 | 3 | Up until now, we've mostly executed futures by using `.await`, which blocks 4 | the current task until a particular `Future` completes. However, real 5 | asynchronous applications often need to execute several different 6 | operations concurrently. 7 | 8 | # Executing Multiple Futures at a Time 9 | 10 | In this chapter, we'll cover some ways to execute multiple asynchronous 11 | operations at the same time: 12 | 13 | - `join!`: waits for futures to all complete 14 | - `select!`: waits for one of several futures to complete 15 | - Spawning: creates a top-level task which ambiently runs a future to completion 16 | - `FuturesUnordered`: a group of futures which yields the result of each subfuture 17 | -------------------------------------------------------------------------------- /src/06_multiple_futures/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # Executing Multiple Futures at a Time 2 | 3 | 到目前为止,我们主要通过`.await`来使用 Futures,它将阻塞当前任务,直到特定的`Future`完成。但是,真正的异步应用程序,通常需要同时执行几个不同的操作。 4 | 5 | # Executing Multiple Futures at a Time 6 | 7 | 在本章中,我们将介绍几种,同时执行多个异步操作的方法: 8 | 9 | - `join!`:等待全部 Futures 完成 10 | - `select!`:等待几种 Futures 之一,完成 11 | - Spawning:创建一个顶级任务,周围运行一个 Future 完成 12 | - `FuturesUnordered`:一组 Future ,yield 回每个子 Future 的结果 13 | -------------------------------------------------------------------------------- /src/06_multiple_futures/02_join.md: -------------------------------------------------------------------------------- 1 | # `join!` 2 | 3 | The `futures::join` macro makes it possible to wait for multiple different 4 | futures to complete while executing them all concurrently. 5 | 6 | # `join!` 7 | 8 | When performing multiple asynchronous operations, it's tempting to simply 9 | `.await` them in a series: 10 | 11 | ```rust 12 | {{#include ../../examples/06_02_join/src/lib.rs:naiive}} 13 | ``` 14 | 15 | However, this will be slower than necessary, since it won't start trying to 16 | `get_music` until after `get_book` has completed. In some other languages, 17 | futures are ambiently run to completion, so two operations can be 18 | run concurrently by first calling the each `async fn` to start the futures, 19 | and then awaiting them both: 20 | 21 | ```rust 22 | {{#include ../../examples/06_02_join/src/lib.rs:other_langs}} 23 | ``` 24 | 25 | However, Rust futures won't do any work until they're actively `.await`ed. 26 | This means that the two code snippets above will both run 27 | `book_future` and `music_future` in series rather than running them 28 | concurrently. To correctly run the two futures concurrently, use 29 | `futures::join!`: 30 | 31 | ```rust 32 | {{#include ../../examples/06_02_join/src/lib.rs:join}} 33 | ``` 34 | 35 | The value returned by `join!` is a tuple containing the output of each 36 | `Future` passed in. 37 | 38 | ## `try_join!` 39 | 40 | For futures which return `Result`, consider using `try_join!` rather than 41 | `join!`. Since `join!` only completes once all subfutures have completed, 42 | it'll continue processing other futures even after one of its subfutures 43 | has returned an `Err`. 44 | 45 | Unlike `join!`, `try_join!` will complete immediately if one of the subfutures 46 | returns an error. 47 | 48 | ```rust 49 | {{#include ../../examples/06_02_join/src/lib.rs:try_join}} 50 | ``` 51 | 52 | Note that the futures passed to `try_join!` must all have the same error type. 53 | Consider using the `.map_err(|e| ...)` and `.err_into()` functions from 54 | `futures::future::TryFutureExt` to consolidate the error types: 55 | 56 | ```rust 57 | {{#include ../../examples/06_02_join/src/lib.rs:try_join_map_err}} 58 | ``` 59 | -------------------------------------------------------------------------------- /src/06_multiple_futures/02_join.zh.md: -------------------------------------------------------------------------------- 1 | # `join!` 2 | 3 | `futures::join`宏的魔力在于,同时执行 Futures 时,等待多个不同的 Futures 完成。 4 | 5 | # `join!` 6 | 7 | 当执行多个异步操作时,一串`.await`,就搞定他们: 8 | 9 | ```rust 10 | {{#include ../../examples/06_02_join/src/lib.rs:naiive}} 11 | ``` 12 | 13 | 但是,这还是比所要的速度慢,因为它不会在`get_book`已经完成之后,开始尝试`get_music`。在其他一些语言中, Future 是环境运行到完成,因此可以,先调用每个`async fn`,来开始 futures,这样两个操作就是同时运行的,然后就是等待两个: 14 | 15 | ```rust 16 | {{#include ../../examples/06_02_join/src/lib.rs:other_langs}} 17 | ``` 18 | 19 | 但是,Rust Futures 在处于`.await`ed 之前不会做任何工作。这意味着,上面的两个代码片段,都将连续运行`book_future`和`music_future`,而不是同时运行它们。要同时正确运行两个 Future ,请使用`futures::join!`: 20 | 21 | ```rust 22 | {{#include ../../examples/06_02_join/src/lib.rs:join}} 23 | ``` 24 | 25 | `join!`传回的值,是一个元组,包含每个传递进去的`Future`的输出。 26 | 27 | ## `try_join!` 28 | 29 | 要想 Futures 返回的是 `Result`,请考虑使用`try_join!`而不是`join!`。只因`join!`仅在所有子 Future 都完成后,才完成,即便是它的其中一个 subfutures 是返回了一个`Err`。 30 | 31 | 不像`join!`,在`try_join!`中,如果其中一个 subfutures 返回一个错误,将立即完成。 32 | 33 | ```rust 34 | {{#include ../../examples/06_02_join/src/lib.rs:try_join}} 35 | ``` 36 | 37 | 请注意, 传递给`try_join!`的 Futures 必须都具有相同的错误类型。考虑使用`futures::future::TryFutureExt`中的`.map_err(|e| ...)`和`.err_into()`函数,来合并错误类型: 38 | 39 | ```rust 40 | {{#include ../../examples/06_02_join/src/lib.rs:try_join_map_err}} 41 | ``` 42 | -------------------------------------------------------------------------------- /src/06_multiple_futures/03_select.md: -------------------------------------------------------------------------------- 1 | # `select!` 2 | 3 | The `futures::select` macro runs multiple futures simultaneously, allowing 4 | the user to respond as soon as any future completes. 5 | 6 | ```rust 7 | {{#include ../../examples/06_03_select/src/lib.rs:example}} 8 | ``` 9 | 10 | The function above will run both `t1` and `t2` concurrently. When either 11 | `t1` or `t2` finishes, the corresponding handler will call `println!`, and 12 | the function will end without completing the remaining task. 13 | 14 | The basic syntax for `select` is ` = => ,`, 15 | repeated for as many futures as you would like to `select` over. 16 | 17 | ## `default => ...` and `complete => ...` 18 | 19 | `select` also supports `default` and `complete` branches. 20 | 21 | A `default` branch will run if none of the futures being `select`ed 22 | over are yet complete. A `select` with a `default` branch will 23 | therefore always return immediately, since `default` will be run 24 | if none of the other futures are ready. 25 | 26 | `complete` branches can be used to handle the case where all futures 27 | being `select`ed over have completed and will no longer make progress. 28 | This is often handy when looping over a `select!`. 29 | 30 | ```rust 31 | {{#include ../../examples/06_03_select/src/lib.rs:default_and_complete}} 32 | ``` 33 | 34 | ## Interaction with `Unpin` and `FusedFuture` 35 | 36 | One thing you may have noticed in the first example above is that we 37 | had to call `.fuse()` on the futures returned by the two `async fn`s, 38 | as well as pinning them with `pin_mut`. Both of these calls are necessary 39 | because the futures used in `select` must implement both the `Unpin` 40 | trait and the `FusedFuture` trait. 41 | 42 | `Unpin` is necessary because the futures used by `select` are not 43 | taken by value, but by mutable reference. By not taking ownership 44 | of the future, uncompleted futures can be used again after the 45 | call to `select`. 46 | 47 | Similarly, the `FusedFuture` trait is required because `select` must 48 | not poll a future after it has completed. `FusedFuture` is implemented 49 | by futures which track whether or not they have completed. This makes 50 | it possible to use `select` in a loop, only polling the futures which 51 | still have yet to complete. This can be seen in the example above, 52 | where `a_fut` or `b_fut` will have completed the second time through 53 | the loop. Because the future returned by `future::ready` implements 54 | `FusedFuture`, it's able to tell `select` not to poll it again. 55 | 56 | Note that streams have a corresponding `FusedStream` trait. Streams 57 | which implement this trait or have been wrapped using `.fuse()` 58 | will yield `FusedFuture` futures from their 59 | `.next()` / `.try_next()` combinators. 60 | 61 | ```rust 62 | {{#include ../../examples/06_03_select/src/lib.rs:fused_stream}} 63 | ``` 64 | 65 | ## Concurrent tasks in a `select` loop with `Fuse` and `FuturesUnordered` 66 | 67 | One somewhat hard-to-discover but handy function is `Fuse::terminated()`, 68 | which allows constructing an empty future which is already terminated, 69 | and can later be filled in with a future that needs to be run. 70 | 71 | This can be handy when there's a task that needs to be run during a `select` 72 | loop but which is created inside the `select` loop itself. 73 | 74 | Note the use of the `.select_next_some()` function. This can be 75 | used with `select` to only run the branch for `Some(_)` values 76 | returned from the stream, ignoring `None`s. 77 | 78 | ```rust 79 | {{#include ../../examples/06_03_select/src/lib.rs:fuse_terminated}} 80 | ``` 81 | 82 | When many copies of the same future need to be run simultaneously, 83 | use the `FuturesUnordered` type. The following example is similar 84 | to the one above, but will run each copy of `run_on_new_num_fut` 85 | to completion, rather than aborting them when a new one is created. 86 | It will also print out a value returned by `run_on_new_num_fut`. 87 | 88 | ```rust 89 | {{#include ../../examples/06_03_select/src/lib.rs:futures_unordered}} 90 | ``` 91 | -------------------------------------------------------------------------------- /src/06_multiple_futures/03_select.zh.md: -------------------------------------------------------------------------------- 1 | # `select!` 2 | 3 | `futures::select`宏能同时运行多个 Future ,从而使用户,可以在任何 Future 完成后,立即做出响应。 4 | 5 | ```rust 6 | {{#include ../../examples/06_03_select/src/lib.rs:example}} 7 | ``` 8 | 9 | 上面的函数将同时运行`t1`和`t2`同时。当`t1`或是`t2`完成后,相应的处理程序将调用`println!`,而该函数将在不完成剩余任务的情况下,结束。 10 | 11 | `select`的基本语法是` = => ,`,重复您想要在`select`上使用的,任意数量的 Future 。 12 | 13 | ## `default => ...` and `complete => ...` 14 | 15 | `select`也支持`default`和`complete`分支。 16 | 17 | 如果`select`了的 Futures 没有一个是完成的,`default`分支将运行。`select`带上一个`default`分支的组合,始终会立即返回,因为`default`在其他 Future 均未准备好,就运行了。 18 | 19 | `complete`分支可以用来处理,`select`ed Future 全部完成,并且将不再前进。对一个`select!`循环访问时通常很方便。 20 | 21 | ```rust 22 | {{#include ../../examples/06_03_select/src/lib.rs:default_and_complete}} 23 | ``` 24 | 25 | ## Interaction with `Unpin` and `FusedFuture` 26 | 27 | 您可能在上面的第一个示例中,注意到的一件事是,我们必须在两个`async fn`返回的 Future 上调用`.fuse()`,以及将用`pin_mut`固定。这两个调用都是必需的,因为在`select`中使用的 futures ,必须同时实现了`Unpin` trait 与`FusedFuture` trait。 28 | 29 | `Unpin`是必要的,因为`select`不是取值的,而是可变的引用。由于不拥有 Future 的所有权,因此可以在对`select`的调用后,未完成的 futures 还可以再次使用。 30 | 31 | 同样,`FusedFuture` trait 是必需的,因为`select`在一个 future 完成后,必不得对它再轮询。`FusedFuture`是 一个 Future trait,作用是追踪 Future 本身是否完成的。这样使得,`select`能在一个循环中使用,只轮询仍未完成的 Futures。可以在上面的示例中看到,其中`a_fut`或是`b_fut`是在循环第二次时完成。因为 `future::ready`返回的 Future 实现了`FusedFuture`,它可以告诉`select`不要再次轮询。 32 | 33 | 请注意,streams 具有对应的`FusedStream` trait。实现此 trait 或由`.fuse()`封装的 Streams,会从他们的 Future `.next()`/`.try_next()`组合器中, yield 出`FusedFuture` futures。 34 | 35 | ```rust 36 | {{#include ../../examples/06_03_select/src/lib.rs:fused_stream}} 37 | ``` 38 | 39 | ## Concurrent tasks in a `select` loop with `Fuse` and `FuturesUnordered` 40 | 41 | 一个有点难以发现但方便的函数是`Fuse::terminated()`,它允许构造一个已经终止的,空的 Future,之后可以用需要运行的 Future 填充它。 42 | 43 | 有个方便的情况就是,有一个任务需要在一个`select`循环内运行,但这个循环又是在这个`select`循环本身里面创建的。 44 | 45 | 注意使用`.select_next_some()`函数。可以与`select`合作,只运行那些由 stream 返回的`Some(_)`值,而忽略`None`s。 46 | 47 | ```rust 48 | {{#include ../../examples/06_03_select/src/lib.rs:fuse_terminated}} 49 | ``` 50 | 51 | 如果需要同时运行多个相同 Future 的副本,请使用`FuturesUnordered`类型。以下示例与上面的示例相似,但是将运行`run_on_new_num_fut`的每个副本,直到完成,而不是在创建新的时,终止它们。还会打印出一个由`run_on_new_num_fut`返回的值。 52 | 53 | ```rust 54 | {{#include ../../examples/06_03_select/src/lib.rs:futures_unordered}} 55 | ``` 56 | -------------------------------------------------------------------------------- /src/07_workarounds/01_chapter.md: -------------------------------------------------------------------------------- 1 | # Workarounds to Know and Love 2 | 3 | Rust's `async` support is still fairly new, and there are a handful of 4 | highly-requested features still under active development, as well 5 | as some subpar diagnostics. This chapter will discuss some common pain 6 | points and explain how to work around them. 7 | -------------------------------------------------------------------------------- /src/07_workarounds/01_chapter.zh.md: -------------------------------------------------------------------------------- 1 | # Workarounds to Know and Love 2 | 3 | Rust 的 `async`支持仍然是相当早期,并且仍在积极开发一些高-要求的功能,以及一些低标准的诊断程序。本章将讨论一些常见的痛点,并解释如何解决它们。 4 | -------------------------------------------------------------------------------- /src/07_workarounds/02_return_type.md: -------------------------------------------------------------------------------- 1 | # Return Type Errors 2 | 3 | In a typical Rust function, returning a value of the wrong type will result 4 | in an error that looks something like this: 5 | 6 | ``` 7 | error[E0308]: mismatched types 8 | --> src/main.rs:2:12 9 | | 10 | 1 | fn foo() { 11 | | - expected `()` because of default return type 12 | 2 | return "foo" 13 | | ^^^^^ expected (), found reference 14 | | 15 | = note: expected type `()` 16 | found type `&'static str` 17 | ``` 18 | 19 | However, the current `async fn` support doesn't know to "trust" the return 20 | type written in the function signature, causing mismatched or even 21 | reversed-sounding errors. For example, the function 22 | `async fn foo() { "foo" }` results in this error: 23 | 24 | ``` 25 | error[E0271]: type mismatch resolving `::Output == ()` 26 | --> src/lib.rs:1:16 27 | | 28 | 1 | async fn foo() { 29 | | ^ expected &str, found () 30 | | 31 | = note: expected type `&str` 32 | found type `()` 33 | = note: the return type of a function must have a statically known size 34 | ``` 35 | 36 | The error says that it *expected* `&str` and found `()`, 37 | which is actually the exact opposite of what you'd want. This is because the 38 | compiler is incorrectly trusting the function body to return the correct type. 39 | 40 | The workaround for this issue is to recognize that errors pointing to the 41 | function signature with the message "expected `SomeType`, found `OtherType`" 42 | usually indicate that one or more return sites are incorrect. 43 | 44 | A fix to this issue is being tracked in [this bug](https://github.com/rust-lang/rust/issues/54326). 45 | 46 | ## `Box` 47 | 48 | Similarly, because the return type from the function signature is not 49 | propagated down correctly, values returned from `async fn` aren't correctly 50 | coerced to their expected type. 51 | 52 | In practice, this means that returning `Box` objects from an 53 | `async fn` requires manually `as`-casting from `Box` to 54 | `Box`. 55 | 56 | This code will result in an error: 57 | 58 | ``` 59 | async fn x() -> Box { 60 | Box::new("foo") 61 | } 62 | ``` 63 | 64 | This issue can be worked around by manually casting using `as`: 65 | 66 | ``` 67 | async fn x() -> Box { 68 | Box::new("foo") as Box 69 | } 70 | ``` 71 | 72 | A fix to this issue is being tracked in [this bug](https://github.com/rust-lang/rust/issues/60424). 73 | -------------------------------------------------------------------------------- /src/07_workarounds/02_return_type.zh.md: -------------------------------------------------------------------------------- 1 | # Return Type Errors 2 | 3 | 在典型的 Rust 函数中,返回的值若是有个错误的类型,将导致出现如下所示的错误: 4 | 5 | ``` 6 | error[E0308]: mismatched types 7 | --> src/main.rs:2:12 8 | | 9 | 1 | fn foo() { 10 | | - expected `()` because of default return type 11 | 2 | return "foo" 12 | | ^^^^^ expected (), found reference 13 | | 14 | = note: expected type `()` 15 | found type `&'static str` 16 | ``` 17 | 18 | 但是,目前`async fn`的支持,还不知道“信任”函数签名中编写的返回类型,从而导致不匹配甚至反标准错误。例如,函数`async fn foo() { "foo" }`导致此错误: 19 | 20 | ``` 21 | error[E0271]: type mismatch resolving `::Output == ()` 22 | --> src/lib.rs:1:16 23 | | 24 | 1 | async fn foo() { 25 | | ^ expected &str, found () 26 | | 27 | = note: expected type `&str` 28 | found type `()` 29 | = note: the return type of a function must have a statically known size 30 | ``` 31 | 32 | 这个错误说得是:它 _expected_ `&str`,但发现了`()`,实际上,这就与您想要的完全相反。这是因为编译器错误地信任,函数主体会返回正确的类型。 33 | 34 | 此问题的变通办法是识别,指向带有"expected `SomeType`, found `OtherType`"信息的函数签名的错误,通常表示一个或多个返回站点不正确。 35 | 36 | Fix in [this bug](https://github.com/rust-lang/rust/issues/54326),可以跟踪浏览下。 37 | 38 | ## `Box` 39 | 40 | 同样,由于函数签名的返回类型,没有正确传播,因此来自`async fn`的值,没有正确地强制使用其预期的类型。 41 | 42 | 实践中,这意味着,`async fn`返回的`Box`对象需要手动`as`,将`Box`转为`Box`。 43 | 44 | 此代码将导致错误: 45 | 46 | ``` 47 | async fn x() -> Box { 48 | Box::new("foo") 49 | } 50 | ``` 51 | 52 | 可以通过使用`as`,这个错误就消除了: 53 | 54 | ``` 55 | async fn x() -> Box { 56 | Box::new("foo") as Box 57 | } 58 | ``` 59 | 60 | Fix in [this bug](https://github.com/rust-lang/rust/issues/60424),可以跟踪浏览。 61 | -------------------------------------------------------------------------------- /src/07_workarounds/03_err_in_async_blocks.md: -------------------------------------------------------------------------------- 1 | # `?` in `async` Blocks 2 | 3 | Just as in `async fn`, it's common to use `?` inside `async` blocks. 4 | However, the return type of `async` blocks isn't explicitly stated. 5 | This can cause the compiler to fail to infer the error type of the 6 | `async` block. 7 | 8 | For example, this code: 9 | 10 | ```rust 11 | let fut = async { 12 | foo().await?; 13 | bar().await?; 14 | Ok(()) 15 | }; 16 | ``` 17 | 18 | will trigger this error: 19 | 20 | ``` 21 | error[E0282]: type annotations needed 22 | --> src/main.rs:5:9 23 | | 24 | 4 | let fut = async { 25 | | --- consider giving `fut` a type 26 | 5 | foo().await?; 27 | | ^^^^^^^^^^^^ cannot infer type 28 | ``` 29 | 30 | Unfortunately, there's currently no way to "give `fut` a type", nor a way 31 | to explicitly specify the return type of an `async` block. 32 | To work around this, use the "turbofish" operator to supply the success and 33 | error types for the `async` block: 34 | 35 | ```rust 36 | let fut = async { 37 | foo().await?; 38 | bar().await?; 39 | Ok::<(), MyError>(()) // <- note the explicit type annotation here 40 | }; 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /src/07_workarounds/03_err_in_async_blocks.zh.md: -------------------------------------------------------------------------------- 1 | # `?` in `async` Blocks 2 | 3 | 就像在`async fn`,`?`在`async`代码块内的使用很常见。但是,`async`代码块的返回类型是没有明确说明的。这可能会导致编译器无法推断`async`代码块的 error 类型。 4 | 5 | 例如,此代码: 6 | 7 | ```rust 8 | let fut = async { 9 | foo().await?; 10 | bar().await?; 11 | Ok(()) 12 | }; 13 | ``` 14 | 15 | 将触发此错误: 16 | 17 | ``` 18 | error[E0282]: type annotations needed 19 | --> src/main.rs:5:9 20 | | 21 | 4 | let fut = async { 22 | | --- consider giving `fut` a type 23 | 5 | foo().await?; 24 | | ^^^^^^^^^^^^ cannot infer type 25 | ``` 26 | 27 | 不幸的是,目前没有办法“giving `fut` a type”(给`fut`一个类型),解决的办法也不是明确指定`async`代码块的返回类型。 28 | 29 | 要解决此问题,请使用“turbofish”操作符,为`async`代码块提供成功和错误类型。: 30 | 31 | ```rust 32 | let fut = async { 33 | foo().await?; 34 | bar().await?; 35 | Ok::<(), MyError>(()) // <- 注意这里的明确类型声明 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /src/07_workarounds/04_send_approximation.md: -------------------------------------------------------------------------------- 1 | # `Send` Approximation 2 | 3 | Some `async fn` state machines are safe to be sent across threads, while 4 | others are not. Whether or not an `async fn` `Future` is `Send` is determined 5 | by whether a non-`Send` type is held across an `.await` point. The compiler 6 | does its best to approximate when values may be held across an `.await` 7 | point, but this analysis is too conservative in a number of places today. 8 | 9 | For example, consider a simple non-`Send` type, perhaps a type 10 | which contains an `Rc`: 11 | 12 | ```rust 13 | use std::rc::Rc; 14 | 15 | #[derive(Default)] 16 | struct NotSend(Rc<()>); 17 | ``` 18 | 19 | Variables of type `NotSend` can briefly appear as temporaries in `async fn`s 20 | even when the resulting `Future` type returned by the `async fn` must be `Send`: 21 | 22 | ```rust 23 | # use std::rc::Rc; 24 | # #[derive(Default)] 25 | # struct NotSend(Rc<()>); 26 | async fn bar() {} 27 | async fn foo() { 28 | NotSend::default(); 29 | bar().await; 30 | } 31 | 32 | fn require_send(_: impl Send) {} 33 | 34 | fn main() { 35 | require_send(foo()); 36 | } 37 | ``` 38 | 39 | However, if we change `foo` to store `NotSend` in a variable, this example no 40 | longer compiles: 41 | 42 | ```rust 43 | # use std::rc::Rc; 44 | # #[derive(Default)] 45 | # struct NotSend(Rc<()>); 46 | async fn foo() { 47 | let x = NotSend::default(); 48 | bar().await; 49 | } 50 | ``` 51 | 52 | ``` 53 | error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely 54 | --> src/main.rs:15:5 55 | | 56 | 15 | require_send(foo()); 57 | | ^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely 58 | | 59 | = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` 60 | = note: required because it appears within the type `NotSend` 61 | = note: required because it appears within the type `{NotSend, impl std::future::Future, ()}` 62 | = note: required because it appears within the type `[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]` 63 | = note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]>` 64 | = note: required because it appears within the type `impl std::future::Future` 65 | = note: required because it appears within the type `impl std::future::Future` 66 | note: required by `require_send` 67 | --> src/main.rs:12:1 68 | | 69 | 12 | fn require_send(_: impl Send) {} 70 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 71 | 72 | error: aborting due to previous error 73 | 74 | For more information about this error, try `rustc --explain E0277`. 75 | ``` 76 | 77 | This error is correct. If we store `x` into a variable, it won't be dropped 78 | until after the `.await`, at which point the `async fn` may be running on 79 | a different thread. Since `Rc` is not `Send`, allowing it to travel across 80 | threads would be unsound. One simple solution to this would be to `drop` 81 | the `Rc` before the `.await`, but unfortunately that does not work today. 82 | 83 | In order to successfully work around this issue, you may have to introduce 84 | a block scope encapsulating any non-`Send` variables. This makes it easier 85 | for the compiler to tell that these variables do not live across an 86 | `.await` point. 87 | 88 | ```rust 89 | # use std::rc::Rc; 90 | # #[derive(Default)] 91 | # struct NotSend(Rc<()>); 92 | async fn foo() { 93 | { 94 | let x = NotSend::default(); 95 | } 96 | bar().await; 97 | } 98 | ``` 99 | -------------------------------------------------------------------------------- /src/07_workarounds/04_send_approximation.zh.md: -------------------------------------------------------------------------------- 1 | # `Send` Approximation 2 | 3 | 一些`async fn`状态机可以安全地越过线程发送,而其他则不能。判断一个`async fn` `Future`是不是`Send`,由非`Send`类型是否越过一个`.await`据点决定的。当可以越过了`.await`据点,编译器会尽力去估计这个是/否。但是今天的许多地方,这种分析都太保守了。 4 | 5 | 例如,考虑一个简单的非`Send`类型,也许包含一个`Rc`: 6 | 7 | ```rust 8 | use std::rc::Rc; 9 | 10 | #[derive(Default)] 11 | struct NotSend(Rc<()>); 12 | ``` 13 | 14 | 类型`NotSend`的变量可以短暂的,像暂时变量一样出现在`async fn`s,即使说`async fn`返回的`Future`类型结果,一定要是`Send`: 15 | 16 | ```rust 17 | # use std::rc::Rc; 18 | # #[derive(Default)] 19 | # struct NotSend(Rc<()>); 20 | async fn bar() {} 21 | async fn foo() { 22 | NotSend::default(); 23 | bar().await; 24 | } 25 | 26 | fn require_send(_: impl Send) {} 27 | 28 | fn main() { 29 | require_send(foo()); 30 | } 31 | ``` 32 | 33 | 但是,如果我们对`foo`修改一下,将`NotSend`存储在一个变量中,那么此示例不再编译: 34 | 35 | ```rust 36 | # use std::rc::Rc; 37 | # #[derive(Default)] 38 | # struct NotSend(Rc<()>); 39 | async fn foo() { 40 | let x = NotSend::default(); 41 | bar().await; 42 | } 43 | ``` 44 | 45 | ``` 46 | error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely 47 | --> src/main.rs:15:5 48 | | 49 | 15 | require_send(foo()); 50 | | ^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely 51 | | 52 | = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` 53 | = note: required because it appears within the type `NotSend` 54 | = note: required because it appears within the type `{NotSend, impl std::future::Future, ()}` 55 | = note: required because it appears within the type `[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]` 56 | = note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]>` 57 | = note: required because it appears within the type `impl std::future::Future` 58 | = note: required because it appears within the type `impl std::future::Future` 59 | note: required by `require_send` 60 | --> src/main.rs:12:1 61 | | 62 | 12 | fn require_send(_: impl Send) {} 63 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 64 | 65 | error: aborting due to previous error 66 | 67 | For more information about this error, try `rustc --explain E0277`. 68 | ``` 69 | 70 | 此错误是正确的。如果我们将`x`存储到一个变量中,在`.await`搞完之前,这个变量都不会 drop,而此时,这个`async fn`有可能在其他线程上运行。因`Rc`不是`Send`,让它越过线程传播是不合理的。一个简单的解决方案是在`.await`之前,就对`Rc`进行`drop`,但是很遗憾,今天这个还不能用。 71 | 72 | 为了成功解决此问题,您可能必须引入一个封装了所有非`Send`的变量。这使编译器更容易知道这些变量,不越过`.await`据点。 73 | 74 | ```rust 75 | # use std::rc::Rc; 76 | # #[derive(Default)] 77 | # struct NotSend(Rc<()>); 78 | async fn foo() { 79 | { 80 | let x = NotSend::default(); 81 | } 82 | bar().await; 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /src/07_workarounds/05_recursion.md: -------------------------------------------------------------------------------- 1 | # Recursion 2 | 3 | Internally, `async fn` creates a state machine type containing each 4 | sub-`Future` being `.await`ed. This makes recursive `async fn`s a little 5 | tricky, since the resulting state machine type has to contain itself: 6 | 7 | ```rust 8 | // This function: 9 | async fn foo() { 10 | step_one().await; 11 | step_two().await; 12 | } 13 | // generates a type like this: 14 | enum Foo { 15 | First(StepOne), 16 | Second(StepTwo), 17 | } 18 | 19 | // So this function: 20 | async fn recursive() { 21 | recursive().await; 22 | recursive().await; 23 | } 24 | 25 | // generates a type like this: 26 | enum Recursive { 27 | First(Recursive), 28 | Second(Recursive), 29 | } 30 | ``` 31 | 32 | This won't work-- we've created an infinitely-sized type! 33 | The compiler will complain: 34 | 35 | ``` 36 | error[E0733]: recursion in an `async fn` requires boxing 37 | --> src/lib.rs:1:22 38 | | 39 | 1 | async fn recursive() { 40 | | ^ an `async fn` cannot invoke itself directly 41 | | 42 | = note: a recursive `async fn` must be rewritten to return a boxed future. 43 | ``` 44 | 45 | In order to allow this, we have to introduce an indirection using `Box`. 46 | Unfortunately, compiler limitations mean that just wrapping the calls to 47 | `recursive()` in `Box::pin` isn't enough. To make this work, we have 48 | to make `recursive` into a non-`async` function which returns a `.boxed()` 49 | `async` block: 50 | 51 | ```rust 52 | {{#include ../../examples/07_05_recursion/src/lib.rs:example}} 53 | ``` 54 | -------------------------------------------------------------------------------- /src/07_workarounds/05_recursion.zh.md: -------------------------------------------------------------------------------- 1 | # Recursion 2 | 3 | 在内部,`async fn`创建一个状态机类型,它包含每个子-`Future`,且都正处于`.await`ed。这使得递归`async fn`有点棘手,因状态机类型的结果必须包含自身: 4 | 5 | ```rust 6 | // 这个函数: 7 | async fn foo() { 8 | step_one().await; 9 | step_two().await; 10 | } 11 | // 生成了一个类型,如下: 12 | enum Foo { 13 | First(StepOne), 14 | Second(StepTwo), 15 | } 16 | 17 | // 所以,这个函数: 18 | async fn recursive() { 19 | recursive().await; 20 | recursive().await; 21 | } 22 | 23 | // 就生成了一个类型,如下: 24 | enum Recursive { 25 | First(Recursive), 26 | Second(Recursive), 27 | } 28 | ``` 29 | 30 | 这行不通——我们创建了一个无限大的类型!编译器会抱怨: 31 | 32 | ``` 33 | error[E0733]: recursion in an `async fn` requires boxing 34 | --> src/lib.rs:1:22 35 | | 36 | 1 | async fn recursive() { 37 | | ^ an `async fn` cannot invoke itself directly 38 | | 39 | = note: a recursive `async fn` must be rewritten to return a boxed future. 40 | ``` 41 | 42 | 为了搞定这一点,我们必须用`Box`剑走偏锋。但不幸的是,编译器的局限性意味着,仅将对`recursive()`的 call 包裹进`Box::pin`,是还不够的,我们必须将`recursive`变成非`async`函数,且它返回一个`.boxed()` 43 | `async`代码块: 44 | 45 | ```rust 46 | {{#include ../../examples/07_05_recursion/src/lib.rs:example}} 47 | ``` 48 | -------------------------------------------------------------------------------- /src/07_workarounds/06_async_in_traits.md: -------------------------------------------------------------------------------- 1 | # `async` in Traits 2 | 3 | Currently, `async fn` cannot be used in traits. The reasons for this are 4 | somewhat complex, but there are plans to remove this restriction in the 5 | future. 6 | 7 | In the meantime, however, this can be worked around using the 8 | [`async_trait` crate from crates.io](https://github.com/dtolnay/async-trait). 9 | 10 | Note that using these trait methods will result in a heap allocation 11 | per-function-call. This is not a significant cost for the vast majority 12 | of applications, but should be considered when deciding whether to use 13 | this functionality in the public API of a low-level function that is expected 14 | to be called millions of times a second. 15 | -------------------------------------------------------------------------------- /src/07_workarounds/06_async_in_traits.zh.md: -------------------------------------------------------------------------------- 1 | # `async` in Traits 2 | 3 | 目前,`async fn`不能用于 trait。造成这种情况的原因有些复杂,但是将来有计划取消此限制。 4 | 5 | 但是,与此同时,可以使用[`async_trait` 箱子,来自 crates.io](https://github.com/dtolnay/async-trait) 合作下。 6 | 7 | 请注意,使用这些 trait 方法将导致 per-function-call(每个函数调用),都搞个分配堆。对于绝大多数应用程序而言,这并不是很大的成本,但是考虑一下,低层函数的公共 API,预计每秒调用数百万次,是否使用此功能。 8 | -------------------------------------------------------------------------------- /src/404.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chinanf-boy/async-book-zh/11d5848250d9598b41a0178eda4f744a67c0f6dc/src/404.md -------------------------------------------------------------------------------- /src/SUMMARY.en.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | - [Getting Started](01_getting_started/01_chapter.md) 4 | - [Why Async?](01_getting_started/02_why_async.md) 5 | - [The State of Asynchronous Rust](01_getting_started/03_state_of_async_rust.md) 6 | - [`async`/`.await` Primer](01_getting_started/04_async_await_primer.md) 7 | - [Applied: HTTP Server](01_getting_started/05_http_server_example.md) 8 | - [Under the Hood: Executing `Future`s and Tasks](02_execution/01_chapter.md) 9 | - [The `Future` Trait](02_execution/02_future.md) 10 | - [Task Wakeups with `Waker`](02_execution/03_wakeups.md) 11 | - [Applied: Build an Executor](02_execution/04_executor.md) 12 | - [Executors and System IO](02_execution/05_io.md) 13 | - [`async`/`await`](03_async_await/01_chapter.md) 14 | - [Pinning](04_pinning/01_chapter.md) 15 | - [Streams](05_streams/01_chapter.md) 16 | - [Iteration and Concurrency](05_streams/02_iteration_and_concurrency.md) 17 | - [Executing Multiple Futures at a Time](06_multiple_futures/01_chapter.md) 18 | - [`join!`](06_multiple_futures/02_join.md) 19 | - [`select!`](06_multiple_futures/03_select.md) 20 | - [TODO: Spawning](404.md) 21 | - [TODO: Cancellation and Timeouts](404.md) 22 | - [TODO: `FuturesUnordered`](404.md) 23 | - [Workarounds to Know and Love](07_workarounds/01_chapter.md) 24 | - [Return Type Errors](07_workarounds/02_return_type.md) 25 | - [`?` in `async` Blocks](07_workarounds/03_err_in_async_blocks.md) 26 | - [`Send` Approximation](07_workarounds/04_send_approximation.md) 27 | - [Recursion](07_workarounds/05_recursion.md) 28 | - [`async` in Traits](07_workarounds/06_async_in_traits.md) 29 | - [TODO: I/O](404.md) 30 | - [TODO: `AsyncRead` and `AsyncWrite`](404.md) 31 | - [TODO: Asynchronous Design Patterns: Solutions and Suggestions](404.md) 32 | - [TODO: Modeling Servers and the Request/Response Pattern](404.md) 33 | - [TODO: Managing Shared State](404.md) 34 | - [TODO: The Ecosystem: Tokio and More](404.md) 35 | - [TODO: Lots, lots more?...](404.md) 36 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | 3 | - [入门](01_getting_started/01_chapter.zh.md) 4 | - [为什么要 async ?](01_getting_started/02_why_async.zh.md) 5 | - [async Rust 状态](01_getting_started/03_state_of_async_rust.zh.md) 6 | - [`async`/`.await`底漆](01_getting_started/04_async_await_primer.zh.md) 7 | - [已应用:HTTP 服务器](01_getting_started/05_http_server_example.zh.md) 8 | - [幕后:执行`Future`和任务](02_execution/01_chapter.zh.md) 9 | - [`Future` Trait](02_execution/02_future.zh.md) 10 | - [任务唤醒`Waker`](02_execution/03_wakeups.zh.md) 11 | - [已应用:生成一个执行器](02_execution/04_executor.zh.md) 12 | - [执行器和系统 IO](02_execution/05_io.zh.md) 13 | - [`async`/`await`](03_async_await/01_chapter.zh.md) 14 | - [Pinning](04_pinning/01_chapter.zh.md) 15 | - [Streams](05_streams/01_chapter.zh.md) 16 | - [迭代与并发](05_streams/02_iteration_and_concurrency.zh.md) 17 | - [一次执行多个 Futures](06_multiple_futures/01_chapter.zh.md) 18 | - [`join!`](06_multiple_futures/02_join.zh.md) 19 | - [`select!`](06_multiple_futures/03_select.zh.md) 20 | - [TODO: Spawning](404.zh.md) 21 | - [TODO:取消和超时](404.zh.md) 22 | - [TODO:`FuturesUnordered`](404.zh.md) 23 | - [走走看看,想想](07_workarounds/01_chapter.zh.md) 24 | - [返回类型错误](07_workarounds/02_return_type.zh.md) 25 | - [`?`在`async`区块](07_workarounds/03_err_in_async_blocks.zh.md) 26 | - [`Send`近似](07_workarounds/04_send_approximation.zh.md) 27 | - [递归](07_workarounds/05_recursion.zh.md) 28 | - [`async`在 Traits 上](07_workarounds/06_async_in_traits.zh.md) 29 | - [TODO:I/O](404.zh.md) 30 | - [TODO:`AsyncRead`以及`AsyncWrite`](404.zh.md) 31 | - [TODO:async 设计模式:解决方案和建议](404.zh.md) 32 | - [TODO:建模服务器和请求/响应模式](404.zh.md) 33 | - [TODO:管理共享状态](404.zh.md) 34 | - [TODO: 生态系统:Tokio 等](404.zh.md) 35 | - [TODO: 多多,多得多的东西?...](404.zh.md) 36 | -------------------------------------------------------------------------------- /sync-en.sh: -------------------------------------------------------------------------------- 1 | cat './.mds-list' | while read line || [[ -n ${line} ]] 2 | do 3 | testseq="zh.md" 4 | if [[ $line =~ $testseq || "$line" == "" ]]; then 5 | echo "skip $line" 6 | else 7 | lowline=`echo "$line" | awk '{print tolower($0)}'` 8 | # lowwer string 9 | zh=${line//source\//} 10 | dir=$(dirname $zh) 11 | 12 | source_readme="./source/readme.md" 13 | if [[ $lowline == $source_readme ]];then 14 | # source/[readme|REAMDE].md => en.md 15 | filename="en.md" 16 | else 17 | # source/other.md => ./other.md 18 | filename=$(basename $zh) 19 | fi 20 | echo "$line >> $dir/$filename" 21 | mkdir -p $dir && cp $line "$_/$filename" 22 | fi 23 | done -------------------------------------------------------------------------------- /theme/custom.css: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | 5 | /* table td:first-child { 6 | width: 65%; 7 | } 8 | 9 | table td:nth-child(2) { 10 | width: 20%; 11 | padding: 3px; 12 | } 13 | 14 | table td:nth-child(3) { 15 | padding: 3px; 16 | } */ 17 | 18 | /* underline hyperlinked inline code items */ 19 | a:hover > .hljs { 20 | text-decoration: underline; 21 | } 22 | 23 | /* color hyperlinked inline code items 24 | identically to normal links */ 25 | .rust a > .hljs, 26 | .navy a > .hljs, 27 | .coal a > .hljs { 28 | color: #2b79a2; 29 | } 30 | 31 | .light a > .hljs { 32 | color: #4183c4; 33 | } 34 | -------------------------------------------------------------------------------- /theme/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ title }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {{#each additional_css}} 33 | 34 | {{/each}} 35 | 36 | {{#if mathjax_support}} 37 | 38 | 40 | {{/if}} 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 62 | 63 | 64 | 71 | 72 | 73 | 83 | 84 | 87 | 88 |
89 | 90 |
91 | {{> header}} 92 | 141 | 142 | {{#if search_enabled}} 143 | 155 | {{/if}} 156 | 157 | 158 | 165 | 166 | 167 | 171 | 172 | 173 |
174 |
175 | {{{ content }}} 176 |
177 | 178 | 196 |
197 |
198 | 199 | 214 | 215 |
216 | 217 | {{#if livereload}} 218 | 219 | 232 | {{/if}} 233 | 234 | {{#if google_analytics}} 235 | 236 | 237 | 238 | 248 | {{/if}} 249 | 250 | {{#if playpen_js}} 251 | 252 | 253 | 254 | 255 | 256 | {{/if}} 257 | 258 | {{#if search_js}} 259 | 260 | 261 | 262 | {{/if}} 263 | 264 | 265 | 266 | 267 | 268 | 269 | {{#each additional_js}} 270 | 271 | {{/each}} 272 | 273 | {{#if is_print}} 274 | {{#if mathjax_support}} 275 | 282 | {{else}} 283 | 288 | {{/if}} 289 | {{/if}} 290 | 291 | 292 | 293 | --------------------------------------------------------------------------------