From 0ec7296ff72026f0880b7d2fe329996294c70cdd Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 15:32:03 -0600 Subject: [PATCH 01/32] Create crate for The Rust Programming Language --- .gitignore | 1 + CONTRIBUTING.md | 11 +++ Cargo.lock | 7 ++ Cargo.toml | 7 ++ LICENSE-APACHE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ README.md | 9 +++ src/lib.rs | 14 ++++ 8 files changed, 275 insertions(+) create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..bd14bf21d7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# Contributing + +## 🚧 Under construction! 🚧 + +Thanks for your interesting in helping us with this! At the moment, we are not +ready for contributions, though. + +Once we stabilize the contents of the book, including the APIs we are +re-exporting here and the little bits of functionality implemented in that +crate, we will gladly take all the help we can get for maintaining this. We will +update this document once we are ready for contributions. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..0858db7638 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "trpl" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..6b6ea9d5ad --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trpl" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000000..38634daab0 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2010 The Rust Project Developers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000000..25597d5838 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2010 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..ddfc445a50 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# The Rust Programming Language Book Crate + +This repository is the home of the `trpl` crate used in _The Rust Programming +Language_ book materials. + +## Requirements + + +This crate currently requires at least Rust 1.77. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..7d12d9af81 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From fcd470c4a8bcd295414eff072c107ff24955ab9c Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 15:32:03 -0600 Subject: [PATCH 02/32] Add Tokio dependency We will primarily just be re-exporting this, though we may also have a couple cases where we choose to implement something small around it. --- Cargo.lock | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + 2 files changed, 423 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0858db7638..826cde189c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,427 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "trpl" version = "0.1.0" +dependencies = [ + "tokio", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml index 6b6ea9d5ad..fe35925a69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +tokio = { version = "1", features = ["full"] } From 3943e512f8f47050e6ca77d98a2386baa18d94d0 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 15:45:50 -0600 Subject: [PATCH 03/32] Re-export `tokio::main` as `trpl::async_main` and test it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Introduce an integration tests crate. Structure it the way people *should* for large crates (although this is not that) and document why, including linking to a relevant post. 2. Add a basic integration test that verifies the re-export works as it should. (This is not exactly rocket science, but we want to make sure these things don’t just stop working on accident.) An open question that remains here: do we want some structure to the crate beyond the top level re-exports? My inclination at this moment is: no, because we don’t have any *motivation* for that, and naming things is difficult. (We cannot do `trpl::async`, for example, because `async` is a keyword!) --- src/lib.rs | 15 +-------------- tests/integration/main.rs | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 tests/integration/main.rs diff --git a/src/lib.rs b/src/lib.rs index 7d12d9af81..766a9a7e68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub use tokio::main as async_main; diff --git a/tests/integration/main.rs b/tests/integration/main.rs new file mode 100644 index 0000000000..81653f155b --- /dev/null +++ b/tests/integration/main.rs @@ -0,0 +1,24 @@ +//! Integration tests for the crate. +//! +//! These all live in a *single* integration test crate, `tests/integration`, +//! because each integration test is a dedicated binary crate which has to be +//! compiled separately. While that is not really a problem for a crate this +//! small, we have chosen to follow this “best practice” here as a good example. +//! +//! For more details on why you might prefer this pattern see [this post][post]. +//! +//! [post]: https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html + +use trpl::async_main; + +#[test] +fn re_exported_macro_works() { + #[async_main] + async fn demo() -> &'static str { + let val = async { "Hello" }.await; + assert_eq!(val, "Hello", "Async is usable in async_main function"); + val + } + + assert_eq!(demo(), "Hello", "value returns correctly"); +} From e51828957d05fbb1bbd2961f122f53288d601247 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 16:13:12 -0600 Subject: [PATCH 04/32] Add more crate-level docs and matching README materials. --- README.md | 12 ++++++++++++ src/lib.rs | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/README.md b/README.md index ddfc445a50..a074e05a6e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,18 @@ This repository is the home of the `trpl` crate used in _The Rust Programming Language_ book materials. +This crate mostly just re-exports items from *other* crates. It exists for two +main reasons: + +1. So that as you read along in _The Rust Programming Language_, you can add + just one dependency, rather than however many we end up with, and likewise + use only one set of imports. + +2. So that we can more easily guarantee it keeps building and working. Since we + control the contents of this crate and when it changes, readers will never be + broken by upstream changes, e.g. if Tokio does a breaking 2.0 release at some + point. + ## Requirements diff --git a/src/lib.rs b/src/lib.rs index 766a9a7e68..a0fddb1b09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,15 @@ +//! A support crate for _The Rust Programming Language_. +//! +//! This crate mostly just re-exports items from *other* crates. It exists for +//! two main reasons: +//! +//! 1. So that as you read along in _The Rust Programming Language_, you can +//! add just one dependency, rather than however many we end up with, and +//! likewise use only one set of imports. +//! +//! 2. So that we can more easily guarantee it keeps building and working. Since +//! we control the contents of this crate and when it changes, readers will +//! never be broken by upstream changes, e.g. if Tokio does a breaking 2.0 +//! release at some point. + pub use tokio::main as async_main; From 608c35b52f8cc9a24b47b159eff35740e55281b9 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 16:13:12 -0600 Subject: [PATCH 05/32] Document the first test so people can understand it. --- tests/integration/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/integration/main.rs b/tests/integration/main.rs index 81653f155b..fd232ccc7e 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -11,14 +11,19 @@ use trpl::async_main; +/// This test makes sure the re-exported version of the `tokio::main` macro, +/// which is applied like `#[tokio::main] async fn some_fn() { … }`, continues +/// to work. However, tests cannot use `async fn`, so to test it, we need to +/// have a non-`async` test function, which then applies the macro to an `async` +/// function in its body, and invokes *that*. #[test] fn re_exported_macro_works() { #[async_main] - async fn demo() -> &'static str { + async fn internal() -> &'static str { let val = async { "Hello" }.await; assert_eq!(val, "Hello", "Async is usable in async_main function"); val } - assert_eq!(demo(), "Hello", "value returns correctly"); + assert_eq!(internal(), "Hello", "value returns correctly"); } From 71ea12e43041c2b135dabb6a461c3c50b7eb67e1 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 16:27:04 -0600 Subject: [PATCH 06/32] Add CI for `main` and PRs against `main` --- .github/workflows/main.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..0642f451b8 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + integration_test: + name: Integration Tests + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose From 4255d87df5337ae1ed0e533eee4ddfc6cfacf7a2 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 16:55:47 -0600 Subject: [PATCH 07/32] Add a CI status badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a074e05a6e..3906da15a4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # The Rust Programming Language Book Crate +![Build Status](https://github.com/chriskrycho/trpl-crate/workflows/CI/badge.svg) + This repository is the home of the `trpl` crate used in _The Rust Programming Language_ book materials. From 7c40849115d92e165c37609d0251f1b12945cb0b Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 16 Apr 2024 15:36:29 -0600 Subject: [PATCH 08/32] Add new Chapter 17: Async and Await --- src/SUMMARY.md | 42 ++++++++++--------- src/ch17-00-async-await.md | 2 + src/{ch17-00-oop.md => ch18-00-oop.md} | 0 ...01-what-is-oo.md => ch18-01-what-is-oo.md} | 0 ...it-objects.md => ch18-02-trait-objects.md} | 0 ...terns.md => ch18-03-oo-design-patterns.md} | 0 ...h18-00-patterns.md => ch19-00-patterns.md} | 0 ...=> ch19-01-all-the-places-for-patterns.md} | 0 ...efutability.md => ch19-02-refutability.md} | 0 ...rn-syntax.md => ch19-03-pattern-syntax.md} | 0 ...atures.md => ch20-00-advanced-features.md} | 0 ...-unsafe-rust.md => ch20-01-unsafe-rust.md} | 0 ...d-traits.md => ch20-03-advanced-traits.md} | 0 ...ced-types.md => ch20-04-advanced-types.md} | 0 ...h20-05-advanced-functions-and-closures.md} | 0 src/{ch19-06-macros.md => ch20-06-macros.md} | 0 ... => ch21-00-final-project-a-web-server.md} | 0 ...threaded.md => ch21-01-single-threaded.md} | 0 ...tithreaded.md => ch21-02-multithreaded.md} | 0 ... ch21-03-graceful-shutdown-and-cleanup.md} | 0 20 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 src/ch17-00-async-await.md rename src/{ch17-00-oop.md => ch18-00-oop.md} (100%) rename src/{ch17-01-what-is-oo.md => ch18-01-what-is-oo.md} (100%) rename src/{ch17-02-trait-objects.md => ch18-02-trait-objects.md} (100%) rename src/{ch17-03-oo-design-patterns.md => ch18-03-oo-design-patterns.md} (100%) rename src/{ch18-00-patterns.md => ch19-00-patterns.md} (100%) rename src/{ch18-01-all-the-places-for-patterns.md => ch19-01-all-the-places-for-patterns.md} (100%) rename src/{ch18-02-refutability.md => ch19-02-refutability.md} (100%) rename src/{ch18-03-pattern-syntax.md => ch19-03-pattern-syntax.md} (100%) rename src/{ch19-00-advanced-features.md => ch20-00-advanced-features.md} (100%) rename src/{ch19-01-unsafe-rust.md => ch20-01-unsafe-rust.md} (100%) rename src/{ch19-03-advanced-traits.md => ch20-03-advanced-traits.md} (100%) rename src/{ch19-04-advanced-types.md => ch20-04-advanced-types.md} (100%) rename src/{ch19-05-advanced-functions-and-closures.md => ch20-05-advanced-functions-and-closures.md} (100%) rename src/{ch19-06-macros.md => ch20-06-macros.md} (100%) rename src/{ch20-00-final-project-a-web-server.md => ch21-00-final-project-a-web-server.md} (100%) rename src/{ch20-01-single-threaded.md => ch21-01-single-threaded.md} (100%) rename src/{ch20-02-multithreaded.md => ch21-02-multithreaded.md} (100%) rename src/{ch20-03-graceful-shutdown-and-cleanup.md => ch21-03-graceful-shutdown-and-cleanup.md} (100%) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index b4b58afdee..a004483b1e 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -101,29 +101,31 @@ - [Shared-State Concurrency](ch16-03-shared-state.md) - [Extensible Concurrency with the `Sync` and `Send` Traits](ch16-04-extensible-concurrency-sync-and-send.md) -- [Object Oriented Programming Features of Rust](ch17-00-oop.md) - - [Characteristics of Object-Oriented Languages](ch17-01-what-is-oo.md) - - [Using Trait Objects That Allow for Values of Different Types](ch17-02-trait-objects.md) - - [Implementing an Object-Oriented Design Pattern](ch17-03-oo-design-patterns.md) +- [Async and Await](ch17-00-async-await.md) + +- [Object Oriented Programming Features of Rust](ch18-00-oop.md) + - [Characteristics of Object-Oriented Languages](ch18-01-what-is-oo.md) + - [Using Trait Objects That Allow for Values of Different Types](ch18-02-trait-objects.md) + - [Implementing an Object-Oriented Design Pattern](ch18-03-oo-design-patterns.md) ## Advanced Topics -- [Patterns and Matching](ch18-00-patterns.md) - - [All the Places Patterns Can Be Used](ch18-01-all-the-places-for-patterns.md) - - [Refutability: Whether a Pattern Might Fail to Match](ch18-02-refutability.md) - - [Pattern Syntax](ch18-03-pattern-syntax.md) - -- [Advanced Features](ch19-00-advanced-features.md) - - [Unsafe Rust](ch19-01-unsafe-rust.md) - - [Advanced Traits](ch19-03-advanced-traits.md) - - [Advanced Types](ch19-04-advanced-types.md) - - [Advanced Functions and Closures](ch19-05-advanced-functions-and-closures.md) - - [Macros](ch19-06-macros.md) - -- [Final Project: Building a Multithreaded Web Server](ch20-00-final-project-a-web-server.md) - - [Building a Single-Threaded Web Server](ch20-01-single-threaded.md) - - [Turning Our Single-Threaded Server into a Multithreaded Server](ch20-02-multithreaded.md) - - [Graceful Shutdown and Cleanup](ch20-03-graceful-shutdown-and-cleanup.md) +- [Patterns and Matching](ch19-00-patterns.md) + - [All the Places Patterns Can Be Used](ch19-01-all-the-places-for-patterns.md) + - [Refutability: Whether a Pattern Might Fail to Match](ch19-02-refutability.md) + - [Pattern Syntax](ch19-03-pattern-syntax.md) + +- [Advanced Features](ch20-00-advanced-features.md) + - [Unsafe Rust](ch20-01-unsafe-rust.md) + - [Advanced Traits](ch20-03-advanced-traits.md) + - [Advanced Types](ch20-04-advanced-types.md) + - [Advanced Functions and Closures](ch20-05-advanced-functions-and-closures.md) + - [Macros](ch20-06-macros.md) + +- [Final Project: Building a Multithreaded Web Server](ch21-00-final-project-a-web-server.md) + - [Building a Single-Threaded Web Server](ch21-01-single-threaded.md) + - [Turning Our Single-Threaded Server into a Multithreaded Server](ch21-02-multithreaded.md) + - [Graceful Shutdown and Cleanup](ch21-03-graceful-shutdown-and-cleanup.md) - [Appendix](appendix-00.md) - [A - Keywords](appendix-01-keywords.md) diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md new file mode 100644 index 0000000000..7f216e6f3a --- /dev/null +++ b/src/ch17-00-async-await.md @@ -0,0 +1,2 @@ +## Async and Await + diff --git a/src/ch17-00-oop.md b/src/ch18-00-oop.md similarity index 100% rename from src/ch17-00-oop.md rename to src/ch18-00-oop.md diff --git a/src/ch17-01-what-is-oo.md b/src/ch18-01-what-is-oo.md similarity index 100% rename from src/ch17-01-what-is-oo.md rename to src/ch18-01-what-is-oo.md diff --git a/src/ch17-02-trait-objects.md b/src/ch18-02-trait-objects.md similarity index 100% rename from src/ch17-02-trait-objects.md rename to src/ch18-02-trait-objects.md diff --git a/src/ch17-03-oo-design-patterns.md b/src/ch18-03-oo-design-patterns.md similarity index 100% rename from src/ch17-03-oo-design-patterns.md rename to src/ch18-03-oo-design-patterns.md diff --git a/src/ch18-00-patterns.md b/src/ch19-00-patterns.md similarity index 100% rename from src/ch18-00-patterns.md rename to src/ch19-00-patterns.md diff --git a/src/ch18-01-all-the-places-for-patterns.md b/src/ch19-01-all-the-places-for-patterns.md similarity index 100% rename from src/ch18-01-all-the-places-for-patterns.md rename to src/ch19-01-all-the-places-for-patterns.md diff --git a/src/ch18-02-refutability.md b/src/ch19-02-refutability.md similarity index 100% rename from src/ch18-02-refutability.md rename to src/ch19-02-refutability.md diff --git a/src/ch18-03-pattern-syntax.md b/src/ch19-03-pattern-syntax.md similarity index 100% rename from src/ch18-03-pattern-syntax.md rename to src/ch19-03-pattern-syntax.md diff --git a/src/ch19-00-advanced-features.md b/src/ch20-00-advanced-features.md similarity index 100% rename from src/ch19-00-advanced-features.md rename to src/ch20-00-advanced-features.md diff --git a/src/ch19-01-unsafe-rust.md b/src/ch20-01-unsafe-rust.md similarity index 100% rename from src/ch19-01-unsafe-rust.md rename to src/ch20-01-unsafe-rust.md diff --git a/src/ch19-03-advanced-traits.md b/src/ch20-03-advanced-traits.md similarity index 100% rename from src/ch19-03-advanced-traits.md rename to src/ch20-03-advanced-traits.md diff --git a/src/ch19-04-advanced-types.md b/src/ch20-04-advanced-types.md similarity index 100% rename from src/ch19-04-advanced-types.md rename to src/ch20-04-advanced-types.md diff --git a/src/ch19-05-advanced-functions-and-closures.md b/src/ch20-05-advanced-functions-and-closures.md similarity index 100% rename from src/ch19-05-advanced-functions-and-closures.md rename to src/ch20-05-advanced-functions-and-closures.md diff --git a/src/ch19-06-macros.md b/src/ch20-06-macros.md similarity index 100% rename from src/ch19-06-macros.md rename to src/ch20-06-macros.md diff --git a/src/ch20-00-final-project-a-web-server.md b/src/ch21-00-final-project-a-web-server.md similarity index 100% rename from src/ch20-00-final-project-a-web-server.md rename to src/ch21-00-final-project-a-web-server.md diff --git a/src/ch20-01-single-threaded.md b/src/ch21-01-single-threaded.md similarity index 100% rename from src/ch20-01-single-threaded.md rename to src/ch21-01-single-threaded.md diff --git a/src/ch20-02-multithreaded.md b/src/ch21-02-multithreaded.md similarity index 100% rename from src/ch20-02-multithreaded.md rename to src/ch21-02-multithreaded.md diff --git a/src/ch20-03-graceful-shutdown-and-cleanup.md b/src/ch21-03-graceful-shutdown-and-cleanup.md similarity index 100% rename from src/ch20-03-graceful-shutdown-and-cleanup.md rename to src/ch21-03-graceful-shutdown-and-cleanup.md From c1a0d1434e304b2673cc2729598cfc19e922b60a Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 16 Apr 2024 15:36:29 -0600 Subject: [PATCH 09/32] Ch. 17: introduction section --- src/ch17-00-async-await.md | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md index 7f216e6f3a..219fbeecca 100644 --- a/src/ch17-00-async-await.md +++ b/src/ch17-00-async-await.md @@ -1,2 +1,48 @@ ## Async and Await +In Chapter 16, we saw one of Rust’s approaches to concurrency: using threads. +Since Rust 1.39, there has been another option for concurrency: the async-await +model. + +In the async-await model, concurrent operations do not require their own +threads. Instead, they can run on *tasks*. A task is a bit like a thread, but +instead of being managed by the operating system, it is managed by a runtime. +The job of a runtime is to schedule Some languages, including Go, Kotlin, +Erlang, and Swift, ship runtimes with the language. In Rust, there are many +different runtimes, because the things a runtime for a high-throughput web +server should do are very different from the things a runtime for a +microcontroller should do. + +In the rest of chapter, we will: + +* see how to use Rust’s `async` and `.await` syntax +* explore how to use the async-await model to solve some of the same challenges + we looked at in Chapter 16 +* look at how multithreading and async provide complementary solutions, which + you can even use together in many cases + +First, though, let’s explore what async-await gives us. + +### Why async-await + +Many operations we ask the computer to do can take a while to finish. For +example, if you used a video editor to create a video of a family celebration, +exporting it could take anywhere from minutes to hours. Similarly, when you +upload that video to some service to share it with your family, that upload +process might take a long time. + +It would be nice if we could do something else while we are waiting for those +long-running processes to complete. + +As we saw in the previous chapter, threads provide one approach to concurrency, +and they let us solve some of these issues. However, they also have some +tradeoffs. On many operating systems, they use a fair bit of memory for each +thread, and they come with some overhead for starting up and shutting down. +Threads are also only an option when your operating system and hardware support +multiple threads. While mainstream desktop and mobile operating systems have all +had threading for many years, many embedded operating systems used on +microcontrollers do not. + + + +The async-await model provides a different, complementary set of tradeoffs. From d17c7129822a689b9a23472c9019836235dd56c9 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 19 Apr 2024 15:45:51 -0600 Subject: [PATCH 10/32] Ch. 17: Expand introduction and add stub for tasks chapter --- src/ch17-00-async-await.md | 75 ++++++++++++++++++++++++++------------ src/ch17-01-tasks.md | 21 +++++++++++ 2 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 src/ch17-01-tasks.md diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md index 219fbeecca..94a0c3b04c 100644 --- a/src/ch17-00-async-await.md +++ b/src/ch17-00-async-await.md @@ -4,22 +4,13 @@ In Chapter 16, we saw one of Rust’s approaches to concurrency: using threads. Since Rust 1.39, there has been another option for concurrency: the async-await model. -In the async-await model, concurrent operations do not require their own -threads. Instead, they can run on *tasks*. A task is a bit like a thread, but -instead of being managed by the operating system, it is managed by a runtime. -The job of a runtime is to schedule Some languages, including Go, Kotlin, -Erlang, and Swift, ship runtimes with the language. In Rust, there are many -different runtimes, because the things a runtime for a high-throughput web -server should do are very different from the things a runtime for a -microcontroller should do. - In the rest of chapter, we will: * see how to use Rust’s `async` and `.await` syntax * explore how to use the async-await model to solve some of the same challenges we looked at in Chapter 16 -* look at how multithreading and async provide complementary solutions, which - you can even use together in many cases +* look at how multithreading and async-await provide complementary solutions, + which you can even use together in many cases First, though, let’s explore what async-await gives us. @@ -29,20 +20,58 @@ Many operations we ask the computer to do can take a while to finish. For example, if you used a video editor to create a video of a family celebration, exporting it could take anywhere from minutes to hours. Similarly, when you upload that video to some service to share it with your family, that upload -process might take a long time. +process might take a long time. It would be nice if we could do something else +while we are waiting for those long-running processes to complete. + +In the previous chapter we treated parallelism and concurrency as +interchangeable. Now we need to distinguish between the two a little more: + +* *Parallelism* is when operations can happen simultaneously. + +* *Concurrency* is when operations can make progress without having to wait for + all other operations to complete. + +On a machine with multiple CPU cores, we can actually do work in parallel. One +core can be doing thing while another core does something completely unrelated, +and those actually happen at the same time. On a machine with a single CPU core, +the CPU can only do one operation at a time, but we can still have concurrency. +Using tools like threads, processes, and async-await, the computer can pause one +activity and switch to others before eventually cycling back to that first +activity again. So all parallel operations are also concurrent, but not all +concurrent operations happen in parallel! + +> Note: When working with async-await in Rust, we need to think in terms of +> *concurrency*. Depending on the hardware, the operating system, and the async +> runtime we are using, that concurrency may use some degree of parallelism +> under the hood, or it may not. -It would be nice if we could do something else while we are waiting for those -long-running processes to complete. +One common analogy for thinking about the difference between concurrency and +parallelism is cooking in a kitchen. Parallelism is like having two cooks: one +working on cooking eggs, and the other working on preparing fruit bowls. Those +can happen at the same time, without either affecting the other. Concurrency is +like having a single cook who can start cooking some eggs, start dicing up some +vegetables to use in the omelette, adding seasoning and whatever vegetables are +ready to the eggs at certain points, and switching back and forth between those +tasks. -As we saw in the previous chapter, threads provide one approach to concurrency, -and they let us solve some of these issues. However, they also have some -tradeoffs. On many operating systems, they use a fair bit of memory for each -thread, and they come with some overhead for starting up and shutting down. -Threads are also only an option when your operating system and hardware support -multiple threads. While mainstream desktop and mobile operating systems have all -had threading for many years, many embedded operating systems used on -microcontrollers do not. +(This analogy breaks down if you think about it too hard. The eggs keep cooking +while the cook is chopping up the vegetables, after all. That is parallelism, +not concurrency! The focus of the analogy is the *cook*, not the food, though, +and as long as you keep that in mind, it mostly works.) +Consider again the examples of exporting a video file and waiting on the video +file to finish uploading. The video export will use as much CPU and GPU power as +it can. If you only had one CPU core, and your operating system never paused +that export until it completed, you could not do anything else on your computer +while it was running. That would be a pretty frustrating experience, though, so +instead your computer could invisibly interrupt the export often enough to let +you get other small amounts of work done along the way. +The file upload is different. That does not take up that much CPU time. Mostly, +you are waiting on data to transfer across the network. -The async-await model provides a different, complementary set of tradeoffs. +A big difference between the cooking analogy and Rust’s async-await model for +concurrency is that in the cooking example, the cook makes the decision about +when to switch tasks. In Rust’s async-await model, the tasks are in control of +that. To see how, let’s look at diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md new file mode 100644 index 0000000000..bef9b40cba --- /dev/null +++ b/src/ch17-01-tasks.md @@ -0,0 +1,21 @@ +## Tasks + +As we saw in the previous chapter, threads provide one approach to concurrency, +and they let us solve some of these issues. However, they also have some +tradeoffs. On many operating systems, they use a fair bit of memory for each +thread, and they come with some overhead for starting up and shutting down. +Threads are also only an option when your operating system and hardware support +multiple threads. While mainstream desktop and mobile operating systems have all +had threading for many years, many embedded operating systems used on +microcontrollers do not. + +The async-await model provides a different, complementary set of tradeoffs. + +In the async-await model, concurrent operations do not require their own +threads. Instead, they can run on *tasks*. A task is a bit like a thread, but +instead of being managed by the operating system, it is managed by a runtime. +The job of a runtime is to schedule Some languages, including Go, Kotlin, +Erlang, and Swift, ship runtimes with the language. In Rust, there are many +different runtimes, because the things a runtime for a high-throughput web +server should do are very different from the things a runtime for a +microcontroller should do. From 4ce722d6ca8a95c4c709c55fb80818145fa535bb Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 19 Apr 2024 16:39:59 -0600 Subject: [PATCH 11/32] Ch. 17: Fix a couple typos and incomplete sentences --- src/ch17-00-async-await.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md index 94a0c3b04c..0f6c7e9980 100644 --- a/src/ch17-00-async-await.md +++ b/src/ch17-00-async-await.md @@ -32,13 +32,13 @@ interchangeable. Now we need to distinguish between the two a little more: all other operations to complete. On a machine with multiple CPU cores, we can actually do work in parallel. One -core can be doing thing while another core does something completely unrelated, -and those actually happen at the same time. On a machine with a single CPU core, -the CPU can only do one operation at a time, but we can still have concurrency. -Using tools like threads, processes, and async-await, the computer can pause one -activity and switch to others before eventually cycling back to that first -activity again. So all parallel operations are also concurrent, but not all -concurrent operations happen in parallel! +core can be doing one thing while another core does something completely +unrelated, and those actually happen at the same time. On a machine with a +single CPU core, the CPU can only do one operation at a time, but we can still +have concurrency. Using tools like threads, processes, and async-await, the +computer can pause one activity and switch to others before eventually cycling +back to that first activity again. So all parallel operations are also +concurrent, but not all concurrent operations happen in parallel! > Note: When working with async-await in Rust, we need to think in terms of > *concurrency*. Depending on the hardware, the operating system, and the async @@ -56,8 +56,8 @@ tasks. (This analogy breaks down if you think about it too hard. The eggs keep cooking while the cook is chopping up the vegetables, after all. That is parallelism, -not concurrency! The focus of the analogy is the *cook*, not the food, though, -and as long as you keep that in mind, it mostly works.) +not just concurrency! The focus of the analogy is the *cook*, not the food, +though, and as long as you keep that in mind, it mostly works.) Consider again the examples of exporting a video file and waiting on the video file to finish uploading. The video export will use as much CPU and GPU power as @@ -74,4 +74,4 @@ here --> A big difference between the cooking analogy and Rust’s async-await model for concurrency is that in the cooking example, the cook makes the decision about when to switch tasks. In Rust’s async-await model, the tasks are in control of -that. To see how, let’s look at +that. To see how, let’s look at how Rust actually uses async-await. From 71d92ecc1a9e870a3b12f3559e7ad565753a06f5 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Mon, 22 Apr 2024 12:08:04 -0600 Subject: [PATCH 12/32] Ch. 17: Add some introductory material about async/await MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 17.00: Introduction to the conceptual machinery. This is very nascent but has some decent bones. - 17.01: Trying to get at the foundations for tasks, laziness, etc.; this is *especially* incomplete. - 17.02: Just a paragraph I do not want to lose, which I think *will* be useful… eventually. --- src/ch16-01-threads.md | 3 +- src/ch17-00-async-await.md | 52 ++++---- src/ch17-01-tasks.md | 239 +++++++++++++++++++++++++++++++++++-- src/ch17-02.md | 4 + 4 files changed, 270 insertions(+), 28 deletions(-) create mode 100644 src/ch17-02.md diff --git a/src/ch16-01-threads.md b/src/ch16-01-threads.md index cfdd0c7066..a26a18dc03 100644 --- a/src/ch16-01-threads.md +++ b/src/ch16-01-threads.md @@ -30,7 +30,8 @@ operating systems provide an API the language can call for creating new threads. The Rust standard library uses a *1:1* model of thread implementation, whereby a program uses one operating system thread per one language thread. There are crates that implement other models of threading that make different -tradeoffs to the 1:1 model. +tradeoffs to the 1:1 model. (Rust’s async-await system, which we will see in the +next chapter, provides another approach to concurrency as well.) ### Creating a New Thread with `spawn` diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md index 0f6c7e9980..a5bd2eb6e4 100644 --- a/src/ch17-00-async-await.md +++ b/src/ch17-00-async-await.md @@ -31,20 +31,6 @@ interchangeable. Now we need to distinguish between the two a little more: * *Concurrency* is when operations can make progress without having to wait for all other operations to complete. -On a machine with multiple CPU cores, we can actually do work in parallel. One -core can be doing one thing while another core does something completely -unrelated, and those actually happen at the same time. On a machine with a -single CPU core, the CPU can only do one operation at a time, but we can still -have concurrency. Using tools like threads, processes, and async-await, the -computer can pause one activity and switch to others before eventually cycling -back to that first activity again. So all parallel operations are also -concurrent, but not all concurrent operations happen in parallel! - -> Note: When working with async-await in Rust, we need to think in terms of -> *concurrency*. Depending on the hardware, the operating system, and the async -> runtime we are using, that concurrency may use some degree of parallelism -> under the hood, or it may not. - One common analogy for thinking about the difference between concurrency and parallelism is cooking in a kitchen. Parallelism is like having two cooks: one working on cooking eggs, and the other working on preparing fruit bowls. Those @@ -59,17 +45,43 @@ while the cook is chopping up the vegetables, after all. That is parallelism, not just concurrency! The focus of the analogy is the *cook*, not the food, though, and as long as you keep that in mind, it mostly works.) +On a machine with multiple CPU cores, we can actually do work in parallel. One +core can be doing one thing while another core does something completely +unrelated, and those actually happen at the same time. On a machine with a +single CPU core, the CPU can only do one operation at a time, but we can still +have concurrency. Using tools like threads, processes, and async-await, the +computer can pause one activity and switch to others before eventually cycling +back to that first activity again. So all parallel operations are also +concurrent, but not all concurrent operations happen in parallel! + +> Note: When working with async-await in Rust, we need to think in terms of +> *concurrency*. Depending on the hardware, the operating system, and the async +> runtime we are using, that concurrency may use some degree of parallelism +> under the hood, or it may not. More about async runtimes in a later section! + Consider again the examples of exporting a video file and waiting on the video file to finish uploading. The video export will use as much CPU and GPU power as it can. If you only had one CPU core, and your operating system never paused that export until it completed, you could not do anything else on your computer while it was running. That would be a pretty frustrating experience, though, so -instead your computer could invisibly interrupt the export often enough to let -you get other small amounts of work done along the way. - -The file upload is different. That does not take up that much CPU time. Mostly, -you are waiting on data to transfer across the network. +instead your computer can (and does!) invisibly interrupt the export often +enough to let you get other small amounts of work done along the way. + +The file upload is different. It does not take up very much CPU time. Instead, +you are mostly waiting on data to transfer across the network. If you only have +a single CPU core, you might write a bunch of data to a network socket and then +wait for it to finish getting sent by the network controller. You could choose +to wait for all the data to get “flushed” from the socket and actually sent over +the network, but if there is a busy network connection, you might be waiting for +a while… with your CPU doing not much! Thus, even if you make a blocking call to +write to a socket, your computer probably does other things while the network +operation is happening. + +In both of these cases, it might be useful for *your program* to participate in +the same kind of concurrency the computer is providing for the rest of the +system. One way to do this is the approach we saw last chapter: using threads, +which are provided and managed by the operating system. Another way to get +access to concurrency is using language-specific capabilities—like async-await. A big difference between the cooking analogy and Rust’s async-await model for concurrency is that in the cooking example, the cook makes the decision about diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index bef9b40cba..701a720bb1 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -1,4 +1,6 @@ -## Tasks +## Futures and the Async-Await Syntax + +### Tasks As we saw in the previous chapter, threads provide one approach to concurrency, and they let us solve some of these issues. However, they also have some @@ -9,13 +11,236 @@ multiple threads. While mainstream desktop and mobile operating systems have all had threading for many years, many embedded operating systems used on microcontrollers do not. -The async-await model provides a different, complementary set of tradeoffs. +The async-await model provides a different, complementary set of tradeoffs. In + + In the async-await model, concurrent operations do not require their own threads. Instead, they can run on *tasks*. A task is a bit like a thread, but instead of being managed by the operating system, it is managed by a runtime. -The job of a runtime is to schedule Some languages, including Go, Kotlin, -Erlang, and Swift, ship runtimes with the language. In Rust, there are many -different runtimes, because the things a runtime for a high-throughput web -server should do are very different from the things a runtime for a -microcontroller should do. + + + +### + +Like many other languages with first-class support for the async-await +programming model, Rust uses the `async` and `await` keywords—though with some +important differences from other languages like C# or JavaScript. Blocks and +functions can be marked `async`, and you can wait on the result of an `async` +function or block to resolve using the `await` keyword. + +Let’s write our first async function: + +```rust +fn main() { + hello_async(); +} + +async fn hello_async() { + println!("Hello, async!"); +} +``` + +If we compile and run this… nothing happens, and we get a compiler warning: + +```console +$ cargo run +warning: unused implementer of `Future` that must be used + --> src/main.rs:2:5 + | +2 | hello_async(); + | ^^^^^^^^^^^^^ + | + = note: futures do nothing unless you `.await` or poll them + = note: `#[warn(unused_must_use)]` on by default + +warning: `hello-async` (bin "hello-async") generated 1 warning + Finished dev [unoptimized + debuginfo] target(s) in 1.50s + Running `target/debug/hello-async` +``` + +The warning tells us why nothing happened. Calling `hello_async()` itself was +not enough: we need to `.await`or poll the “future” it returns. That might be a +bit surprising: we did not write a return type on the function. However, we +*did* mark it as an `async fn`. In Rust, `async fn` is equivalent to writing a +function which returns a *future* of the return type, using the `impl Trait` +syntax we discussed back in the [“Traits as Parameters”][impl-trait] section in +Chapter 10. So these two are roughly equivalent: + + +```rust +fn hello_async() -> impl Future { + println!("Hello, async!"); +} +``` + +```rust +async fn hello_async() { + println!("Hello, async!"); +} +``` + +That explains why we got the `unused_must_use` warning. The other part of the +warning was the note that we need to `.await` or poll the future. Rust's `await` +keyword is a postfix keyword, meaning it goes *after* the expression you are +awaiting. (As of now, `await` is the only postfix keyword in the language.) +Let’s try that here: + +```rust +fn main() { + hello_async().await; +} +``` + +Now we actually have a compiler error! + +```text +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> src/main.rs:2:19 + | +1 | fn main() { + | ---- this is not `async` +2 | hello_async().await; + | ^^^^^ only allowed inside `async` functions and blocks +``` + +Okay, so we cannot actually use `.await` in `main`, because it is not an `async` +function itself—and it cannot be. To understand why, we need to pause to see +what a `Future` actually is and why it needs to be `.await`-ed or polled to do +anything. + +### Understanding `Future` + +Since `async fn` compiles to a return type with `impl Future`, we +know that `Future` is a trait, with an associated type `Output`. The other part +of the trait is its one method: `poll`. The `poll` method returns a fairly +simple type: + +```rust +enum Poll { + Ready(T), + Pending +} +``` + +(You might have noticed that this `Poll` type is a lot like an `Option`. Having +a dedicated type lets Rust treat `Poll` differently from `Option`, though, which +is important since they have very different meanings!) + +Under the hood, when you call `.await`, Rust compiles that to code which calls +`poll` instead, kind of like this: + + +```rust +match hello_async().poll() { + Ready(_) => { + // We’re done! + } + Pending => { + // But what goes here? + } +} +``` + +As you can see from this sample, though, there is a question: what happens when +the `Future` is still `Pending`? We need some way to try again. We would need to +have something like this instead: + + +```rust +let hello_async_fut = hello_async(); +loop { + match hello_async_fut.poll() { + Ready(_) => { + break; + } + Pending => { + // continue + } + } +} +``` + +If we wrote that code, though, we would block the computer from doing anything +else, though—the opposite of what we were going for. It also wouldn’t compile +for three other reasons, though: + +1. The `poll` method actually requires an argument: a `Context` that carries + along a way to say when to call it again, to avoid exactly the problem of + blocking other operations from making progress. + +2. The `Future` returned by our `async fn` is not actually ready to use here, + because `poll` requires that the future be “pinned”—guaranteed not to get + moved around in memory, so that any references to the future can be checked + for memory safety. + +3. Even if we pinned the future, this code would move `hello_async_fut` in the + first iteration through the `loop`. After the first time through the loop, we + would not be able to call it again. + +When we use the `async fn` form with `.await`, Rust compiles it to something +smarter than the `loop` above, in conjunction with a *runtime* responsible for +executing that loop. + +Some languages, including Go, Kotlin, Erlang, and Swift, ship runtimes with the +language. In Rust, there are many different runtimes, because the things a +runtime for a high-throughput web server with dozens of CPU cores and terabytes +of RAM should do are very different from the things a runtime for a +microcontroller with a single core and one gigabyte of RAM should do. + + + +The other thing to notice here is that futures in Rust are *lazy*. They do not +do anything until you explicitly ask them to—whether by calling `poll` or by +using `.await` to do so. This is different from the behavior we saw when using +`thread::spawn` in the previous chapter, and it is different from how many other +languages approach async-await. This allows Rust to avoid running async code +unless it is actually needed, and supports some of the memory safety features +Rust brings to async-await. (The details are beyond the scope of this book, but +are well worth digging into.) + +### Running Async Code + + + +Going back to `main`, this explains why we cannot have an `async fn main`: what +would execute the async code? We need to pick a runtime and executor. We can get +started with that easily by using the simple one that comes bundled with the +`futures` crate, an official home for Rust experimentation for async code. Since +we will be using a bunch of tools from that crate for the rest of the chapter, +let’s go ahead and add it to the dependencies for our test project: + +``` +cargo add futures@0.3 +``` + +Now we can use the executor which comes with `futures` to run the code. The +`futures::executor::block_on` function takes in a `Future` and runs it until it +completes. + +```rust +use futures::executor; + +fn main() { + executor::block_on(hello_async()); +} + +async fn hello_async() { + println!("Hello, async!"); +} +``` + +Now when we run this, we get the behavior we might have expected initially: + +```console +$ cargo run + Compiling hello-async v0.1.0 (/Users/chris/dev/chriskrycho/async-trpl-fun/hello-async) + Finished dev [unoptimized + debuginfo] target(s) in 4.89s + Running `target/debug/hello-async` +Hello, async! +``` + +Now, that’s a lot of work to just print a string, but we have laid some key +foundations for working with async and await in Rust. + +[impl-trait]: ch10-02-traits.html#traits-as-parameters diff --git a/src/ch17-02.md b/src/ch17-02.md new file mode 100644 index 0000000000..75d4dcaa65 --- /dev/null +++ b/src/ch17-02.md @@ -0,0 +1,4 @@ +The executor from `futures` we used in the previous section is intentionally +quite limited. It works well, but if you are going to build a real-world +application, you will want to use the executor from one of the *other* runtimes +in the Rust ecosystem. From fdb3794e2af67a037493e54259be98442b908f59 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 23 Apr 2024 17:26:07 -0600 Subject: [PATCH 13/32] Ch. 17: Introduce runtimes and the `trpl` crate Add a fair bit more material about the `futures` executor and why we might prefer to use something else. With that motivation in place, have the readers add our `trpl` crate. (We can of course rename that crate, but it does the job for now.) Use it to get the equivalent of `#[tokio::main]` equivalent and incorporate it into an example. --- src/ch17-01-tasks.md | 3 ++- src/ch17-02.md | 51 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 701a720bb1..70ed2f2647 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -241,6 +241,7 @@ Hello, async! ``` Now, that’s a lot of work to just print a string, but we have laid some key -foundations for working with async and await in Rust. +foundations for working with async-await in Rust! Now that you know the basics +of how futures work, and the [impl-trait]: ch10-02-traits.html#traits-as-parameters diff --git a/src/ch17-02.md b/src/ch17-02.md index 75d4dcaa65..80c1a6f582 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -1,4 +1,49 @@ The executor from `futures` we used in the previous section is intentionally -quite limited. It works well, but if you are going to build a real-world -application, you will want to use the executor from one of the *other* runtimes -in the Rust ecosystem. +quite limited. It works quite well, but if you are going to build a real-world +application, you will likely want to use the executor from one of the *other* +runtimes in the Rust ecosystem. + +For the rest of the chapter, we will be using [Tokio][tokio], which is the most +widely used async runtime, especially (but not only!) for web applications. + +> Note: There are other great options out there, too, and they may be more +> suitable for your purposes, so this is not at all saying Tokio is better than +> the alternatives. + +To keep this chapter focused on learning async-await, rather than juggling parts +of the ecosystem, we have created the `trpl` crate. (`trpl` is short for “The +Rust Programming Language”). It re-exports all the types, traits, and functions +you will need, and in a couple cases wires up a few things for you which are +less relevant to the subject of the book. There is no magic involved, though! If +you want to understand what the crate does, we encourage you to check out [its +source code][crate-source]. You will be able to see what crate each re-export +comes from, and we have left extensive comments explaining what the handful of +helper functions we supply are doing. + +For now, go ahead and add the dependency to your `hello-async` project: + +```console +$ cargo add trpl +``` + +Okay, now let’s start exploring . Going forward, we will use a new `async_main` +macro so that we can use `async fn` with a `main` function. Under the hood, this +little utility macro from Tokio sets up the Tokio runtime and wires up a call +kind of like we saw with `futures::executor::block_on` in the previous section. + +```rust +use trpl::async_main; + +#[async_main] +async fn main() { + async { + println!("Hello, world!"); + }.await; +} +``` + +Now we can use `async` blocks directly in `main` and not worry about wiring up +the runtime ourselves. + +[tokio]: https://tokio.rs +[crate-source]: TODO From cf00f054baeda0b0fd43fa3905129eec5da5f76e Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 1 May 2024 10:07:55 -0600 Subject: [PATCH 14/32] =?UTF-8?q?Use=20=E2=80=9Casynchronous=20programming?= =?UTF-8?q?=E2=80=9D=20or=20=E2=80=9Casync=E2=80=9D,=20not=20=E2=80=9Casyn?= =?UTF-8?q?c-await=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ch16-01-threads.md | 12 ++++++------ src/ch17-00-async-await.md | 34 +++++++++++++++++----------------- src/ch17-01-tasks.md | 32 ++++++++++++++++---------------- src/ch17-02.md | 12 ++++++------ 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/ch16-01-threads.md b/src/ch16-01-threads.md index a26a18dc03..f3cdbff907 100644 --- a/src/ch16-01-threads.md +++ b/src/ch16-01-threads.md @@ -26,12 +26,12 @@ a code structure that is different from that in programs running in a single thread. Programming languages implement threads in a few different ways, and many -operating systems provide an API the language can call for creating new -threads. The Rust standard library uses a *1:1* model of thread implementation, -whereby a program uses one operating system thread per one language thread. -There are crates that implement other models of threading that make different -tradeoffs to the 1:1 model. (Rust’s async-await system, which we will see in the -next chapter, provides another approach to concurrency as well.) +operating systems provide an API the language can call for creating new threads. +The Rust standard library uses a *1:1* model of thread implementation, whereby a +program uses one operating system thread per one language thread. There are +crates that implement other models of threading that make different tradeoffs to +the 1:1 model. (Rust’s async system, which we will see in the next chapter, +provides another approach to concurrency as well.) ### Creating a New Thread with `spawn` diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md index a5bd2eb6e4..f34caf2e66 100644 --- a/src/ch17-00-async-await.md +++ b/src/ch17-00-async-await.md @@ -1,20 +1,20 @@ ## Async and Await In Chapter 16, we saw one of Rust’s approaches to concurrency: using threads. -Since Rust 1.39, there has been another option for concurrency: the async-await -model. +Since Rust 1.39, there has been another option for concurrency: asynchronous +programming, or *async*. In the rest of chapter, we will: * see how to use Rust’s `async` and `.await` syntax -* explore how to use the async-await model to solve some of the same challenges - we looked at in Chapter 16 -* look at how multithreading and async-await provide complementary solutions, - which you can even use together in many cases +* explore how to use the async model to solve some of the same challenges we + looked at in Chapter 16 +* look at how multithreading and async provide complementary solutions, which + you can even use together in many cases -First, though, let’s explore what async-await gives us. +First, though, let’s explore what async gives us. -### Why async-await +### Why Async? Many operations we ask the computer to do can take a while to finish. For example, if you used a video editor to create a video of a family celebration, @@ -49,12 +49,12 @@ On a machine with multiple CPU cores, we can actually do work in parallel. One core can be doing one thing while another core does something completely unrelated, and those actually happen at the same time. On a machine with a single CPU core, the CPU can only do one operation at a time, but we can still -have concurrency. Using tools like threads, processes, and async-await, the -computer can pause one activity and switch to others before eventually cycling -back to that first activity again. So all parallel operations are also -concurrent, but not all concurrent operations happen in parallel! +have concurrency. Using tools like threads, processes, and async, the computer +can pause one activity and switch to others before eventually cycling back to +that first activity again. So all parallel operations are also concurrent, but +not all concurrent operations happen in parallel! -> Note: When working with async-await in Rust, we need to think in terms of +> Note: When working with async in Rust, we need to think in terms of > *concurrency*. Depending on the hardware, the operating system, and the async > runtime we are using, that concurrency may use some degree of parallelism > under the hood, or it may not. More about async runtimes in a later section! @@ -81,9 +81,9 @@ In both of these cases, it might be useful for *your program* to participate in the same kind of concurrency the computer is providing for the rest of the system. One way to do this is the approach we saw last chapter: using threads, which are provided and managed by the operating system. Another way to get -access to concurrency is using language-specific capabilities—like async-await. +access to concurrency is using language-specific capabilities—like async. -A big difference between the cooking analogy and Rust’s async-await model for +A big difference between the cooking analogy and Rust’s async model for concurrency is that in the cooking example, the cook makes the decision about -when to switch tasks. In Rust’s async-await model, the tasks are in control of -that. To see how, let’s look at how Rust actually uses async-await. +when to switch tasks. In Rust’s async model, the tasks are in control of that. +To see how, let’s look at how Rust actually uses async. diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 70ed2f2647..632638d60d 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -1,4 +1,4 @@ -## Futures and the Async-Await Syntax +## Futures and the Async Syntax ### Tasks @@ -11,23 +11,23 @@ multiple threads. While mainstream desktop and mobile operating systems have all had threading for many years, many embedded operating systems used on microcontrollers do not. -The async-await model provides a different, complementary set of tradeoffs. In +The async model provides a different, complementary set of tradeoffs. In -In the async-await model, concurrent operations do not require their own -threads. Instead, they can run on *tasks*. A task is a bit like a thread, but -instead of being managed by the operating system, it is managed by a runtime. +In the async model, concurrent operations do not require their own threads. +Instead, they can run on *tasks*. A task is a bit like a thread, but instead of +being managed by the operating system, it is managed by a runtime. ### -Like many other languages with first-class support for the async-await -programming model, Rust uses the `async` and `await` keywords—though with some -important differences from other languages like C# or JavaScript. Blocks and -functions can be marked `async`, and you can wait on the result of an `async` -function or block to resolve using the `await` keyword. +Like many other languages with first-class support for the async programming +model, Rust uses the `async` and `await` keywords—though with some important +differences from other languages like C# or JavaScript. Blocks and functions can +be marked `async`, and you can wait on the result of an `async` function or +block to resolve using the `await` keyword. Let’s write our first async function: @@ -194,10 +194,10 @@ The other thing to notice here is that futures in Rust are *lazy*. They do not do anything until you explicitly ask them to—whether by calling `poll` or by using `.await` to do so. This is different from the behavior we saw when using `thread::spawn` in the previous chapter, and it is different from how many other -languages approach async-await. This allows Rust to avoid running async code -unless it is actually needed, and supports some of the memory safety features -Rust brings to async-await. (The details are beyond the scope of this book, but -are well worth digging into.) +languages approach async. This allows Rust to avoid running async code unless it +is actually needed, and supports some of the memory safety features Rust brings +to async. (The details are beyond the scope of this book, but are well worth +digging into.) ### Running Async Code @@ -241,7 +241,7 @@ Hello, async! ``` Now, that’s a lot of work to just print a string, but we have laid some key -foundations for working with async-await in Rust! Now that you know the basics -of how futures work, and the +foundations for working with async in Rust! Now that you know the basics of how +futures work, and the [impl-trait]: ch10-02-traits.html#traits-as-parameters diff --git a/src/ch17-02.md b/src/ch17-02.md index 80c1a6f582..c1cff2e869 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -10,12 +10,12 @@ widely used async runtime, especially (but not only!) for web applications. > suitable for your purposes, so this is not at all saying Tokio is better than > the alternatives. -To keep this chapter focused on learning async-await, rather than juggling parts -of the ecosystem, we have created the `trpl` crate. (`trpl` is short for “The -Rust Programming Language”). It re-exports all the types, traits, and functions -you will need, and in a couple cases wires up a few things for you which are -less relevant to the subject of the book. There is no magic involved, though! If -you want to understand what the crate does, we encourage you to check out [its +To keep this chapter focused on learning async, rather than juggling parts of +the ecosystem, we have created the `trpl` crate. (`trpl` is short for “The Rust +Programming Language”). It re-exports all the types, traits, and functions you +will need, and in a couple cases wires up a few things for you which are less +relevant to the subject of the book. There is no magic involved, though! If you +want to understand what the crate does, we encourage you to check out [its source code][crate-source]. You will be able to see what crate each re-export comes from, and we have left extensive comments explaining what the handful of helper functions we supply are doing. From 02b0bd195ce3306f85749dbdb72a3e70b7911e55 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 1 May 2024 10:12:48 -0600 Subject: [PATCH 15/32] Add note about Async Book ch. 4: Pinning --- src/ch17-01-tasks.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 632638d60d..c20bb5bdb8 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -197,7 +197,8 @@ using `.await` to do so. This is different from the behavior we saw when using languages approach async. This allows Rust to avoid running async code unless it is actually needed, and supports some of the memory safety features Rust brings to async. (The details are beyond the scope of this book, but are well worth -digging into.) +digging into. In particular, see [Chapter 4: Pinning][pinning] in the official +[_Asynchronous Programming in Rust_][async-book] book.) ### Running Async Code @@ -245,3 +246,5 @@ foundations for working with async in Rust! Now that you know the basics of how futures work, and the [impl-trait]: ch10-02-traits.html#traits-as-parameters +[pinning]: https://rust-lang.github.io/async-book/04_pinning/01_chapter.html +[async-book]: https://rust-lang.github.io/async-book/ From 94d95c6e6bffe8f6976e3cd8a2d0e7a9b9da0a2d Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 1 May 2024 10:18:23 -0600 Subject: [PATCH 16/32] Ch. 17: Explain what `Poll::Pending` and `Poll::Ready(T)` mean. --- src/ch17-01-tasks.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index c20bb5bdb8..13ceb1b960 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -123,9 +123,17 @@ enum Poll { } ``` -(You might have noticed that this `Poll` type is a lot like an `Option`. Having -a dedicated type lets Rust treat `Poll` differently from `Option`, though, which -is important since they have very different meanings!) +You might have noticed that this `Poll` type is a lot like an `Option`. Having a +dedicated type lets Rust treat `Poll` differently from `Option`, though, which +is important since they have very different meanings! The `Pending` variant +indicates that the future still has work to do, so the caller will need to check +again later. The `Ready` variant indicates that the `Future` has finished its +work and the `T` value is available. + +> Note: With most future, the caller should not call `poll()` again after the +> future has returned `Ready`. Many futures will panic if polled after becoming +> ready! Futures which are safe to poll again will say so explicitly in their +> documentation. Under the hood, when you call `.await`, Rust compiles that to code which calls `poll` instead, kind of like this: From b4bcb2cb3e431199ad6013723519dbfcefeeed83 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 1 May 2024 11:02:42 -0600 Subject: [PATCH 17/32] Ch. 17: add a TODO for the `async fn` desugaring --- src/ch17-01-tasks.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 13ceb1b960..561a5a10eb 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -67,15 +67,17 @@ function which returns a *future* of the return type, using the `impl Trait` syntax we discussed back in the [“Traits as Parameters”][impl-trait] section in Chapter 10. So these two are roughly equivalent: - ```rust -fn hello_async() -> impl Future { +async fn hello_async() { println!("Hello, async!"); } ``` + + + ```rust -async fn hello_async() { +fn hello_async() -> impl Future { println!("Hello, async!"); } ``` @@ -171,7 +173,7 @@ loop { If we wrote that code, though, we would block the computer from doing anything else, though—the opposite of what we were going for. It also wouldn’t compile -for three other reasons, though: +for three other reasons: 1. The `poll` method actually requires an argument: a `Context` that carries along a way to say when to call it again, to avoid exactly the problem of From 63baf226c424ba265220eee6ab66e679d20f8c6f Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 1 May 2024 11:32:15 -0600 Subject: [PATCH 18/32] Ch. 17: fix a misplaced period --- src/ch17-02.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch17-02.md b/src/ch17-02.md index c1cff2e869..57b002d415 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -11,7 +11,7 @@ widely used async runtime, especially (but not only!) for web applications. > the alternatives. To keep this chapter focused on learning async, rather than juggling parts of -the ecosystem, we have created the `trpl` crate. (`trpl` is short for “The Rust +the ecosystem, we have created the `trpl` crate (`trpl` is short for “The Rust Programming Language”). It re-exports all the types, traits, and functions you will need, and in a couple cases wires up a few things for you which are less relevant to the subject of the book. There is no magic involved, though! If you From c31d308f1836a69d6784bc623b68603c52d919bd Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 1 May 2024 11:32:15 -0600 Subject: [PATCH 19/32] Ch. 17: get to working async code sooner Move up the installation of the futures crate: get the initial example compiling as soon as possible (even though it is just "Hello, world!") and *then* explain what was going on. --- src/ch17-01-tasks.md | 143 ++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 62 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 561a5a10eb..32f8bf3120 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -7,11 +7,12 @@ and they let us solve some of these issues. However, they also have some tradeoffs. On many operating systems, they use a fair bit of memory for each thread, and they come with some overhead for starting up and shutting down. Threads are also only an option when your operating system and hardware support -multiple threads. While mainstream desktop and mobile operating systems have all -had threading for many years, many embedded operating systems used on -microcontrollers do not. +them! While mainstream desktop and mobile operating systems have all had +threading for many years, many embedded operating systems, like those used on +some microcontrollers, do not. -The async model provides a different, complementary set of tradeoffs. In +The async model provides a different—and ultimately complementary—set of +tradeoffs. In @@ -65,7 +66,10 @@ bit surprising: we did not write a return type on the function. However, we *did* mark it as an `async fn`. In Rust, `async fn` is equivalent to writing a function which returns a *future* of the return type, using the `impl Trait` syntax we discussed back in the [“Traits as Parameters”][impl-trait] section in -Chapter 10. So these two are roughly equivalent: +Chapter 10, and an `async` block compiles to an anonymous struct which +implements the `Future` trait. + +So these two are roughly equivalent: ```rust async fn hello_async() { @@ -73,28 +77,41 @@ async fn hello_async() { } ``` - - - ```rust fn hello_async() -> impl Future { - println!("Hello, async!"); + async { + println!("Hello, async!"); + } } ``` -That explains why we got the `unused_must_use` warning. The other part of the -warning was the note that we need to `.await` or poll the future. Rust's `await` -keyword is a postfix keyword, meaning it goes *after* the expression you are -awaiting. (As of now, `await` is the only postfix keyword in the language.) -Let’s try that here: - + + +That explains why we got the `unused_must_use` warning: writing `async fn` meant +we were actually returning an anonymous `Future`. Just like with `Result`, Rust +will let us know if we don’t use a `Future`. It is often a mistake to ignore an +error in an operation your program performed. With a `Future`, it is even more +significant. To see why, let’s look at the other part of the warning. It told us +that “futures do nothing unless you `.await` or poll them”. That is, futures are +*lazy*: they don’t do anything until you ask them to. The compiler is telling us +that ignoring a `Future` makes it completely useless! We will see why that is +later on. For now, let’s start by awaiting the future returned by `hello_async` +to actually have it run. + +> Note: Rust’s `await` keyword goes *after* the expression you are awaiting—that +> is, it is a *postfix keyword*. This is different from what you might be used +> to if you have used async in languages like JavaScript or C#. Rust chose this +> because it makes chains of async and non-async methods much nicer to work +> with. As of now, `await` is the only postfix keyword in the language. + + ```rust fn main() { hello_async().await; } ``` -Now we actually have a compiler error! +Oh no! We have gone from a compiler warning ton an actual error: ```text error[E0728]: `await` is only allowed inside `async` functions and blocks @@ -106,12 +123,48 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks | ^^^^^ only allowed inside `async` functions and blocks ``` -Okay, so we cannot actually use `.await` in `main`, because it is not an `async` -function itself—and it cannot be. To understand why, we need to pause to see -what a `Future` actually is and why it needs to be `.await`-ed or polled to do -anything. +This time, the compiler is informing us we cannot actually use `.await` in +`main`, because `main` is not an `async` function. As of today, it cannot be +without some extra help: it needs a *runtime* to execute the asynchronous code. +For now, we can solve that by adding the runtime built into the `futures` crate, +an official home for Rust experimentation for async code. Since we will be using +a bunch of tools from that crate for the rest of the chapter, let’s go ahead and +add it to the dependencies for our test project: + +``` +cargo add futures@0.3 +``` + +Now we can use the executor which comes with `futures` to run the code. The +`futures::executor::block_on` function takes in a `Future` and runs it until it +completes. + +```rust +use futures::executor; + +fn main() { + executor::block_on(hello_async()); +} + +async fn hello_async() { + println!("Hello, async!"); +} +``` + +Now when we run this, we get the behavior we might have expected initially: + +```console +$ cargo run + Compiling hello-async v0.1.0 (/Users/chris/dev/chriskrycho/async-trpl-fun/hello-async) + Finished dev [unoptimized + debuginfo] target(s) in 4.89s + Running `target/debug/hello-async` +Hello, async! +``` + +Phew: we finally have some working async code! To understand why we needed this, +let’s dig in a little more into what a `Future` actually is. -### Understanding `Future` +### Futures and runtimes Since `async fn` compiles to a return type with `impl Future`, we know that `Future` is a trait, with an associated type `Output`. The other part @@ -132,7 +185,7 @@ indicates that the future still has work to do, so the caller will need to check again later. The `Ready` variant indicates that the `Future` has finished its work and the `T` value is available. -> Note: With most future, the caller should not call `poll()` again after the +> Note: With most futures, the caller should not call `poll()` again after the > future has returned `Ready`. Many futures will panic if polled after becoming > ready! Futures which are safe to poll again will say so explicitly in their > documentation. @@ -192,11 +245,11 @@ When we use the `async fn` form with `.await`, Rust compiles it to something smarter than the `loop` above, in conjunction with a *runtime* responsible for executing that loop. -Some languages, including Go, Kotlin, Erlang, and Swift, ship runtimes with the -language. In Rust, there are many different runtimes, because the things a -runtime for a high-throughput web server with dozens of CPU cores and terabytes -of RAM should do are very different from the things a runtime for a -microcontroller with a single core and one gigabyte of RAM should do. +> Note: Some languages, including Go, Kotlin, Erlang, and Swift, ship runtimes +> with the language. In Rust, there are many different runtimes, because a +> runtime might need to do very different things to support a high-throughput +> web server with dozens of CPU cores and terabytes of RAM than it would for a +> microcontroller with a single core and one gigabyte of RAM should do. @@ -215,41 +268,7 @@ digging into. In particular, see [Chapter 4: Pinning][pinning] in the official Going back to `main`, this explains why we cannot have an `async fn main`: what -would execute the async code? We need to pick a runtime and executor. We can get -started with that easily by using the simple one that comes bundled with the -`futures` crate, an official home for Rust experimentation for async code. Since -we will be using a bunch of tools from that crate for the rest of the chapter, -let’s go ahead and add it to the dependencies for our test project: - -``` -cargo add futures@0.3 -``` - -Now we can use the executor which comes with `futures` to run the code. The -`futures::executor::block_on` function takes in a `Future` and runs it until it -completes. - -```rust -use futures::executor; - -fn main() { - executor::block_on(hello_async()); -} - -async fn hello_async() { - println!("Hello, async!"); -} -``` - -Now when we run this, we get the behavior we might have expected initially: - -```console -$ cargo run - Compiling hello-async v0.1.0 (/Users/chris/dev/chriskrycho/async-trpl-fun/hello-async) - Finished dev [unoptimized + debuginfo] target(s) in 4.89s - Running `target/debug/hello-async` -Hello, async! -``` +would execute the async code? We need to pick a runtime and executor. Now, that’s a lot of work to just print a string, but we have laid some key foundations for working with async in Rust! Now that you know the basics of how From dee72208b8384ec164642e43fe76fba25a3b9112 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Thu, 9 May 2024 16:35:28 -0600 Subject: [PATCH 20/32] Ch. 17: add to table of contents so `mdbook serve` is useful --- src/SUMMARY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a004483b1e..daa9a07af7 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -102,6 +102,8 @@ - [Extensible Concurrency with the `Sync` and `Send` Traits](ch16-04-extensible-concurrency-sync-and-send.md) - [Async and Await](ch17-00-async-await.md) + - [Futures and the Async Syntax](ch17-01-tasks.md) + - [something something tokio?!?](ch17-02.md) - [Object Oriented Programming Features of Rust](ch18-00-oop.md) - [Characteristics of Object-Oriented Languages](ch18-01-what-is-oo.md) From 5b3c27eb4ac60291e808b9f90798cf39709b86ef Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Thu, 9 May 2024 16:51:16 -0600 Subject: [PATCH 21/32] Ch. 17: clarify language about Tokio --- src/ch17-02.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ch17-02.md b/src/ch17-02.md index 57b002d415..bb0306ff2f 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -7,8 +7,8 @@ For the rest of the chapter, we will be using [Tokio][tokio], which is the most widely used async runtime, especially (but not only!) for web applications. > Note: There are other great options out there, too, and they may be more -> suitable for your purposes, so this is not at all saying Tokio is better than -> the alternatives. +> suitable for your purposes. We are using Tokio because it is the most widely +> used runtime—not as a judgment call on whether it is the *best* runtime! To keep this chapter focused on learning async, rather than juggling parts of the ecosystem, we have created the `trpl` crate (`trpl` is short for “The Rust From ebebf8c166ad7026ab4d3840fea455283b8837f9 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Thu, 9 May 2024 16:54:09 -0600 Subject: [PATCH 22/32] Ch. 17: use `trpl`, not `futures`, even for initial code samples - Update the instructions to install `trpl` instead of `futures`, and move the introductory text there. - Update the note about Tokio in 17.02 to describe both `futures` and `tokio`. --- src/ch17-01-tasks.md | 30 ++++++++++++++++++------------ src/ch17-02.md | 40 ++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 32f8bf3120..be642fcec2 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -126,24 +126,29 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks This time, the compiler is informing us we cannot actually use `.await` in `main`, because `main` is not an `async` function. As of today, it cannot be without some extra help: it needs a *runtime* to execute the asynchronous code. -For now, we can solve that by adding the runtime built into the `futures` crate, -an official home for Rust experimentation for async code. Since we will be using -a bunch of tools from that crate for the rest of the chapter, let’s go ahead and -add it to the dependencies for our test project: -``` -cargo add futures@0.3 +To keep this chapter focused on learning async, rather than juggling parts of +the ecosystem, we have created the `trpl` crate (`trpl` is short for “The Rust +Programming Language”). It re-exports all the types, traits, and functions you +will need, and in a couple cases wires up a few things for you which are less +relevant to the subject of the book. There is no magic involved, though! If you +want to understand what the crate does, we encourage you to check out [its +source code][crate-source]. You will be able to see what crate each re-export +comes from, and we have left extensive comments explaining what the handful of +helper functions we supply are doing. + +For now, go ahead and add the dependency to your `hello-async` project: + +```console +$ cargo add trpl ``` -Now we can use the executor which comes with `futures` to run the code. The -`futures::executor::block_on` function takes in a `Future` and runs it until it -completes. +Now we can get our code working by using the `trpl::block_on` function, which +takes in a `Future` and runs it until it completes. ```rust -use futures::executor; - fn main() { - executor::block_on(hello_async()); + trpl::block_on(hello_async()); } async fn hello_async() { @@ -153,6 +158,7 @@ async fn hello_async() { Now when we run this, we get the behavior we might have expected initially: + ```console $ cargo run Compiling hello-async v0.1.0 (/Users/chris/dev/chriskrycho/async-trpl-fun/hello-async) diff --git a/src/ch17-02.md b/src/ch17-02.md index bb0306ff2f..fc650165df 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -3,30 +3,21 @@ quite limited. It works quite well, but if you are going to build a real-world application, you will likely want to use the executor from one of the *other* runtimes in the Rust ecosystem. -For the rest of the chapter, we will be using [Tokio][tokio], which is the most -widely used async runtime, especially (but not only!) for web applications. - -> Note: There are other great options out there, too, and they may be more -> suitable for your purposes. We are using Tokio because it is the most widely -> used runtime—not as a judgment call on whether it is the *best* runtime! - -To keep this chapter focused on learning async, rather than juggling parts of -the ecosystem, we have created the `trpl` crate (`trpl` is short for “The Rust -Programming Language”). It re-exports all the types, traits, and functions you -will need, and in a couple cases wires up a few things for you which are less -relevant to the subject of the book. There is no magic involved, though! If you -want to understand what the crate does, we encourage you to check out [its -source code][crate-source]. You will be able to see what crate each re-export -comes from, and we have left extensive comments explaining what the handful of -helper functions we supply are doing. - -For now, go ahead and add the dependency to your `hello-async` project: - -```console -$ cargo add trpl -``` - -Okay, now let’s start exploring . Going forward, we will use a new `async_main` +> ### The `futures` and `tokio` Crates +> +> Whenever you see code from the `trpl` crate throughout the rest of the +> chapter, it will be re-exporting code from the `futures` and [`tokio`][tokio] crates. +> +> - The `futures` crate is an official home for Rust experimentation for async +> code, and is actually where the `Future` type was originally designed. +> +> - Tokio is the most widely used async runtime in Rust today, especially (but +> not only!) for web applications. There are other great options out there, +> too, and they may be more suitable for your purposes. We are using Tokio +> because it is the most widely-used runtime—not as a judgment call on whether +> it is the *best* runtime! + +Okay, now let’s start exploring. Going forward, we will use a new `async_main` macro so that we can use `async fn` with a `main` function. Under the hood, this little utility macro from Tokio sets up the Tokio runtime and wires up a call kind of like we saw with `futures::executor::block_on` in the previous section. @@ -45,5 +36,6 @@ async fn main() { Now we can use `async` blocks directly in `main` and not worry about wiring up the runtime ourselves. + [tokio]: https://tokio.rs [crate-source]: TODO From ee0704d4736c9aebb2f7808f2c5816504312de38 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Thu, 9 May 2024 16:54:09 -0600 Subject: [PATCH 23/32] Ch. 17: text/code ordering fix in 17.02 --- src/ch17-02.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ch17-02.md b/src/ch17-02.md index fc650165df..9dfa186927 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -21,6 +21,8 @@ Okay, now let’s start exploring. Going forward, we will use a new `async_main` macro so that we can use `async fn` with a `main` function. Under the hood, this little utility macro from Tokio sets up the Tokio runtime and wires up a call kind of like we saw with `futures::executor::block_on` in the previous section. +Now we can use `async` blocks directly in `main` and not worry about wiring up +the runtime ourselves: ```rust use trpl::async_main; @@ -33,8 +35,6 @@ async fn main() { } ``` -Now we can use `async` blocks directly in `main` and not worry about wiring up -the runtime ourselves. [tokio]: https://tokio.rs From 40df04923b9750ae2e72132898759460f2eb482f Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 16:57:00 -0600 Subject: [PATCH 24/32] Prepare to integrate into TRPL repo --- CONTRIBUTING.md => packages/trpl/CONTRIBUTING.md | 0 Cargo.lock => packages/trpl/Cargo.lock | 0 Cargo.toml => packages/trpl/Cargo.toml | 0 LICENSE-APACHE => packages/trpl/LICENSE-APACHE | 0 LICENSE-MIT => packages/trpl/LICENSE-MIT | 0 README.md => packages/trpl/README.md | 0 {src => packages/trpl/src}/lib.rs | 0 {tests => packages/trpl/tests}/integration/main.rs | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename CONTRIBUTING.md => packages/trpl/CONTRIBUTING.md (100%) rename Cargo.lock => packages/trpl/Cargo.lock (100%) rename Cargo.toml => packages/trpl/Cargo.toml (100%) rename LICENSE-APACHE => packages/trpl/LICENSE-APACHE (100%) rename LICENSE-MIT => packages/trpl/LICENSE-MIT (100%) rename README.md => packages/trpl/README.md (100%) rename {src => packages/trpl/src}/lib.rs (100%) rename {tests => packages/trpl/tests}/integration/main.rs (100%) diff --git a/CONTRIBUTING.md b/packages/trpl/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to packages/trpl/CONTRIBUTING.md diff --git a/Cargo.lock b/packages/trpl/Cargo.lock similarity index 100% rename from Cargo.lock rename to packages/trpl/Cargo.lock diff --git a/Cargo.toml b/packages/trpl/Cargo.toml similarity index 100% rename from Cargo.toml rename to packages/trpl/Cargo.toml diff --git a/LICENSE-APACHE b/packages/trpl/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to packages/trpl/LICENSE-APACHE diff --git a/LICENSE-MIT b/packages/trpl/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to packages/trpl/LICENSE-MIT diff --git a/README.md b/packages/trpl/README.md similarity index 100% rename from README.md rename to packages/trpl/README.md diff --git a/src/lib.rs b/packages/trpl/src/lib.rs similarity index 100% rename from src/lib.rs rename to packages/trpl/src/lib.rs diff --git a/tests/integration/main.rs b/packages/trpl/tests/integration/main.rs similarity index 100% rename from tests/integration/main.rs rename to packages/trpl/tests/integration/main.rs From 8f6bcc72404a121c51f2bf9636fa3d917f6a8198 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 10 May 2024 09:09:43 -0600 Subject: [PATCH 25/32] Ch. 17: expand, clarify, and restructure 17.01 - Explicitly section out the discussions of async functions and blocks and the associated `async` and `.await` syntax from defining `Future` and explaining how it works. - Also a bunch of small phrasing tweaks. --- src/ch17-01-tasks.md | 49 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index be642fcec2..9c379c45fb 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -1,7 +1,5 @@ ## Futures and the Async Syntax -### Tasks - As we saw in the previous chapter, threads provide one approach to concurrency, and they let us solve some of these issues. However, they also have some tradeoffs. On many operating systems, they use a fair bit of memory for each @@ -22,15 +20,12 @@ being managed by the operating system, it is managed by a runtime. -### - -Like many other languages with first-class support for the async programming -model, Rust uses the `async` and `await` keywords—though with some important -differences from other languages like C# or JavaScript. Blocks and functions can -be marked `async`, and you can wait on the result of an `async` function or -block to resolve using the `await` keyword. +Like other languages with async, Rust uses the `async` and `await` +keywords—though with some important differences, as we will see. Blocks and +functions can be marked `async`, and you can wait on the result of an `async` +function or block to resolve using the `await` keyword. -Let’s write our first async function: +Let’s write our first async function, and call it: ```rust fn main() { @@ -60,16 +55,21 @@ warning: `hello-async` (bin "hello-async") generated 1 warning Running `target/debug/hello-async` ``` -The warning tells us why nothing happened. Calling `hello_async()` itself was -not enough: we need to `.await`or poll the “future” it returns. That might be a -bit surprising: we did not write a return type on the function. However, we -*did* mark it as an `async fn`. In Rust, `async fn` is equivalent to writing a -function which returns a *future* of the return type, using the `impl Trait` -syntax we discussed back in the [“Traits as Parameters”][impl-trait] section in -Chapter 10, and an `async` block compiles to an anonymous struct which -implements the `Future` trait. +The warning tells us that just calling `hello_async()` was not enough: we also +need to `.await` or poll the future it returns. This raises two important +questions: + +- Given there is no return type on the function, how is it returning a future? +- What is a future? + +### Async functions + +In Rust, `async fn` is equivalent to writing a function which returns a +future of the return type, using the `impl Trait` syntax we discussed back in +the [“Traits as Parameters”][impl-trait] section in Chapter 10. An `async` +block compiles to an anonymous struct which implements the `Future` trait. -So these two are roughly equivalent: +That means these two are roughly equivalent: ```rust async fn hello_async() { @@ -137,7 +137,7 @@ source code][crate-source]. You will be able to see what crate each re-export comes from, and we have left extensive comments explaining what the handful of helper functions we supply are doing. -For now, go ahead and add the dependency to your `hello-async` project: +For now, go ahead and add the `trpl` crate to your `hello-async` project: ```console $ cargo add trpl @@ -167,10 +167,11 @@ $ cargo run Hello, async! ``` -Phew: we finally have some working async code! To understand why we needed this, -let’s dig in a little more into what a `Future` actually is. +Phew: we finally have some working async code! Now we can answer that second +question: what is a future anyway? That will also help us understand why we need +a runtime to make this work. -### Futures and runtimes +### Futures Since `async fn` compiles to a return type with `impl Future`, we know that `Future` is a trait, with an associated type `Output`. The other part @@ -184,7 +185,7 @@ enum Poll { } ``` -You might have noticed that this `Poll` type is a lot like an `Option`. Having a +You may notice that this `Poll` type is a lot like an `Option`. Having a dedicated type lets Rust treat `Poll` differently from `Option`, though, which is important since they have very different meanings! The `Pending` variant indicates that the future still has work to do, so the caller will need to check From ba383e7701b227339c943cfc1d86d8d79c2d5013 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 10 May 2024 14:06:44 -0600 Subject: [PATCH 26/32] Ch. 17: reorganize 17.01 and get a useable intro to Future --- src/ch13-02-iterators.md | 229 +-------------------------------------- src/ch17-01-tasks.md | 139 +++++++++++++----------- src/ch17-02.md | 7 +- 3 files changed, 83 insertions(+), 292 deletions(-) diff --git a/src/ch13-02-iterators.md b/src/ch13-02-iterators.md index 030ebf48eb..9e1cbecfa5 100644 --- a/src/ch13-02-iterators.md +++ b/src/ch13-02-iterators.md @@ -1,228 +1 @@ -## Processing a Series of Items with Iterators - -The iterator pattern allows you to perform some task on a sequence of items in -turn. An iterator is responsible for the logic of iterating over each item and -determining when the sequence has finished. When you use iterators, you don’t -have to reimplement that logic yourself. - -In Rust, iterators are *lazy*, meaning they have no effect until you call -methods that consume the iterator to use it up. For example, the code in -Listing 13-10 creates an iterator over the items in the vector `v1` by calling -the `iter` method defined on `Vec`. This code by itself doesn’t do anything -useful. - -```rust -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-10/src/main.rs:here}} -``` - -Listing 13-10: Creating an iterator - -The iterator is stored in the `v1_iter` variable. Once we’ve created an -iterator, we can use it in a variety of ways. In Listing 3-5 in Chapter 3, we -iterated over an array using a `for` loop to execute some code on each of its -items. Under the hood this implicitly created and then consumed an iterator, -but we glossed over how exactly that works until now. - -In the example in Listing 13-11, we separate the creation of the iterator from -the use of the iterator in the `for` loop. When the `for` loop is called using -the iterator in `v1_iter`, each element in the iterator is used in one -iteration of the loop, which prints out each value. - -```rust -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-11/src/main.rs:here}} -``` - -Listing 13-11: Using an iterator in a `for` loop - -In languages that don’t have iterators provided by their standard libraries, -you would likely write this same functionality by starting a variable at index -0, using that variable to index into the vector to get a value, and -incrementing the variable value in a loop until it reached the total number of -items in the vector. - -Iterators handle all that logic for you, cutting down on repetitive code you -could potentially mess up. Iterators give you more flexibility to use the same -logic with many different kinds of sequences, not just data structures you can -index into, like vectors. Let’s examine how iterators do that. - -### The `Iterator` Trait and the `next` Method - -All iterators implement a trait named `Iterator` that is defined in the -standard library. The definition of the trait looks like this: - -```rust -pub trait Iterator { - type Item; - - fn next(&mut self) -> Option; - - // methods with default implementations elided -} -``` - -Notice this definition uses some new syntax: `type Item` and `Self::Item`, -which are defining an *associated type* with this trait. We’ll talk about -associated types in depth in Chapter 19. For now, all you need to know is that -this code says implementing the `Iterator` trait requires that you also define -an `Item` type, and this `Item` type is used in the return type of the `next` -method. In other words, the `Item` type will be the type returned from the -iterator. - -The `Iterator` trait only requires implementors to define one method: the -`next` method, which returns one item of the iterator at a time wrapped in -`Some` and, when iteration is over, returns `None`. - -We can call the `next` method on iterators directly; Listing 13-12 demonstrates -what values are returned from repeated calls to `next` on the iterator created -from the vector. - -Filename: src/lib.rs - -```rust,noplayground -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-12/src/lib.rs:here}} -``` - -Listing 13-12: Calling the `next` method on an -iterator - -Note that we needed to make `v1_iter` mutable: calling the `next` method on an -iterator changes internal state that the iterator uses to keep track of where -it is in the sequence. In other words, this code *consumes*, or uses up, the -iterator. Each call to `next` eats up an item from the iterator. We didn’t need -to make `v1_iter` mutable when we used a `for` loop because the loop took -ownership of `v1_iter` and made it mutable behind the scenes. - -Also note that the values we get from the calls to `next` are immutable -references to the values in the vector. The `iter` method produces an iterator -over immutable references. If we want to create an iterator that takes -ownership of `v1` and returns owned values, we can call `into_iter` instead of -`iter`. Similarly, if we want to iterate over mutable references, we can call -`iter_mut` instead of `iter`. - -### Methods that Consume the Iterator - -The `Iterator` trait has a number of different methods with default -implementations provided by the standard library; you can find out about these -methods by looking in the standard library API documentation for the `Iterator` -trait. Some of these methods call the `next` method in their definition, which -is why you’re required to implement the `next` method when implementing the -`Iterator` trait. - -Methods that call `next` are called *consuming adaptors*, because calling them -uses up the iterator. One example is the `sum` method, which takes ownership of -the iterator and iterates through the items by repeatedly calling `next`, thus -consuming the iterator. As it iterates through, it adds each item to a running -total and returns the total when iteration is complete. Listing 13-13 has a -test illustrating a use of the `sum` method: - -Filename: src/lib.rs - -```rust,noplayground -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-13/src/lib.rs:here}} -``` - -Listing 13-13: Calling the `sum` method to get the total -of all items in the iterator - -We aren’t allowed to use `v1_iter` after the call to `sum` because `sum` takes -ownership of the iterator we call it on. - -### Methods that Produce Other Iterators - -*Iterator adaptors* are methods defined on the `Iterator` trait that don’t -consume the iterator. Instead, they produce different iterators by changing -some aspect of the original iterator. - -Listing 13-14 shows an example of calling the iterator adaptor method `map`, -which takes a closure to call on each item as the items are iterated through. -The `map` method returns a new iterator that produces the modified items. The -closure here creates a new iterator in which each item from the vector will be -incremented by 1: - -Filename: src/main.rs - -```rust,not_desired_behavior -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-14/src/main.rs:here}} -``` - -Listing 13-14: Calling the iterator adaptor `map` to -create a new iterator - -However, this code produces a warning: - -```console -{{#include ../listings/ch13-functional-features/listing-13-14/output.txt}} -``` - -The code in Listing 13-14 doesn’t do anything; the closure we’ve specified -never gets called. The warning reminds us why: iterator adaptors are lazy, and -we need to consume the iterator here. - -To fix this warning and consume the iterator, we’ll use the `collect` method, -which we used in Chapter 12 with `env::args` in Listing 12-1. This method -consumes the iterator and collects the resulting values into a collection data -type. - -In Listing 13-15, we collect the results of iterating over the iterator that’s -returned from the call to `map` into a vector. This vector will end up -containing each item from the original vector incremented by 1. - -Filename: src/main.rs - -```rust -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-15/src/main.rs:here}} -``` - -Listing 13-15: Calling the `map` method to create a new -iterator and then calling the `collect` method to consume the new iterator and -create a vector - -Because `map` takes a closure, we can specify any operation we want to perform -on each item. This is a great example of how closures let you customize some -behavior while reusing the iteration behavior that the `Iterator` trait -provides. - -You can chain multiple calls to iterator adaptors to perform complex actions in -a readable way. But because all iterators are lazy, you have to call one of the -consuming adaptor methods to get results from calls to iterator adaptors. - -### Using Closures that Capture Their Environment - -Many iterator adapters take closures as arguments, and commonly the closures -we’ll specify as arguments to iterator adapters will be closures that capture -their environment. - -For this example, we’ll use the `filter` method that takes a closure. The -closure gets an item from the iterator and returns a `bool`. If the closure -returns `true`, the value will be included in the iteration produced by -`filter`. If the closure returns `false`, the value won’t be included. - -In Listing 13-16, we use `filter` with a closure that captures the `shoe_size` -variable from its environment to iterate over a collection of `Shoe` struct -instances. It will return only shoes that are the specified size. - -Filename: src/lib.rs - -```rust,noplayground -{{#rustdoc_include ../listings/ch13-functional-features/listing-13-16/src/lib.rs}} -``` - -Listing 13-16: Using the `filter` method with a closure -that captures `shoe_size` - -The `shoes_in_size` function takes ownership of a vector of shoes and a shoe -size as parameters. It returns a vector containing only shoes of the specified -size. - -In the body of `shoes_in_size`, we call `into_iter` to create an iterator -that takes ownership of the vector. Then we call `filter` to adapt that -iterator into a new iterator that only contains elements for which the closure -returns `true`. - -The closure captures the `shoe_size` parameter from the environment and -compares the value with each shoe’s size, keeping only shoes of the size -specified. Finally, calling `collect` gathers the values returned by the -adapted iterator into a vector that’s returned by the function. - -The test shows that when we call `shoes_in_size`, we get back only shoes -that have the same size as the value we specified. +# Processing a Series of Items with Iterators diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 9c379c45fb..b634c052f7 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -91,12 +91,16 @@ That explains why we got the `unused_must_use` warning: writing `async fn` meant we were actually returning an anonymous `Future`. Just like with `Result`, Rust will let us know if we don’t use a `Future`. It is often a mistake to ignore an error in an operation your program performed. With a `Future`, it is even more -significant. To see why, let’s look at the other part of the warning. It told us -that “futures do nothing unless you `.await` or poll them”. That is, futures are -*lazy*: they don’t do anything until you ask them to. The compiler is telling us -that ignoring a `Future` makes it completely useless! We will see why that is -later on. For now, let’s start by awaiting the future returned by `hello_async` -to actually have it run. +significant. The compiler also warned us that “futures do nothing unless you +`.await` or poll them”. That is, futures are *lazy*: they don’t do anything +until you ask them to. + +The compiler is telling us that ignoring a `Future` makes it completely useless! +This is different from the behavior we saw when using `thread::spawn` in the +previous chapter, and it is different from how many other languages approach +async. This allows Rust to avoid running async code unless it is actually +needed. We will see why that is later on. For now, let’s start by awaiting the +future returned by `hello_async` to actually have it run. > Note: Rust’s `await` keyword goes *after* the expression you are awaiting—that > is, it is a *postfix keyword*. This is different from what you might be used @@ -111,7 +115,7 @@ fn main() { } ``` -Oh no! We have gone from a compiler warning ton an actual error: +Oh no! We have gone from a compiler warning to an actual error: ```text error[E0728]: `await` is only allowed inside `async` functions and blocks @@ -123,9 +127,19 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks | ^^^^^ only allowed inside `async` functions and blocks ``` -This time, the compiler is informing us we cannot actually use `.await` in -`main`, because `main` is not an `async` function. As of today, it cannot be -without some extra help: it needs a *runtime* to execute the asynchronous code. +This time, the compiler is informing us we cannot use `.await` in `main`, +because `main` is not an `async` function. That is because async code needs a +*runtime*: a Rust crate which manages the details of executing the asynchronous +code, including whether or not to use threads for it, scheduling different async +operations, and so on. + +Most languages which support async, including C#, JavaScript, Go, Kotlin, +Erlang, and Swift, bundle a runtime with the language. At least for now, Rust +does not. Instead, there are many different async runtimes available, each of +which makes different tradeoffs suitable to the use case they target. For +example, a high-throughput web server with dozens of CPU cores and terabytes of +RAM has very different different needs than a microcontroller with a single +core, one gigabyte of RAM, and no ability to do heap allocations. To keep this chapter focused on learning async, rather than juggling parts of the ecosystem, we have created the `trpl` crate (`trpl` is short for “The Rust @@ -143,8 +157,9 @@ For now, go ahead and add the `trpl` crate to your `hello-async` project: $ cargo add trpl ``` -Now we can get our code working by using the `trpl::block_on` function, which -takes in a `Future` and runs it until it completes. +In our `main` function, let’s wrap the call to `hello_async` with the +`trpl::block_on` function, which takes in a `Future` and runs it until it +completes. ```rust fn main() { @@ -156,7 +171,7 @@ async fn hello_async() { } ``` -Now when we run this, we get the behavior we might have expected initially: +When we run this, we get the behavior we might have expected initially: ```console @@ -169,14 +184,31 @@ Hello, async! Phew: we finally have some working async code! Now we can answer that second question: what is a future anyway? That will also help us understand why we need -a runtime to make this work. +that `trpl::block_on` call to make this work. ### Futures -Since `async fn` compiles to a return type with `impl Future`, we -know that `Future` is a trait, with an associated type `Output`. The other part -of the trait is its one method: `poll`. The `poll` method returns a fairly -simple type: +A *future* is a data structure which represents the state of some async +operation. More precisely, a Rust `Future` is a trait; it allows many different +data structures to represent different async operations in different ways, but +with a common interface. Here is the definition of the trait: + +```rust +pub trait Future { + type Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; +} +``` + +`Future` has an associated type, `Output`, which says what the result of the +future will be when it resolves. (This is analogous to the `Item` associated +type for the `Iterator` trait, which we saw back in Chapter 13.) Beyond that, +`Future` has only one method: `poll`, which takes a special `Pin` reference for +its `self` parameter and a mutable reference to some `Context` type, and returns +a `Poll`. We will talk a little more about `Pin` and `Context` +later in the chapter. For now, let’s focus on what the method returns, the +`Poll` type: ```rust enum Poll { @@ -198,7 +230,7 @@ work and the `T` value is available. > documentation. Under the hood, when you call `.await`, Rust compiles that to code which calls -`poll` instead, kind of like this: +`poll`, kind of like this: ```rust @@ -231,51 +263,33 @@ loop { } ``` -If we wrote that code, though, we would block the computer from doing anything -else, though—the opposite of what we were going for. It also wouldn’t compile -for three other reasons: - -1. The `poll` method actually requires an argument: a `Context` that carries - along a way to say when to call it again, to avoid exactly the problem of - blocking other operations from making progress. - -2. The `Future` returned by our `async fn` is not actually ready to use here, - because `poll` requires that the future be “pinned”—guaranteed not to get - moved around in memory, so that any references to the future can be checked - for memory safety. - -3. Even if we pinned the future, this code would move `hello_async_fut` in the - first iteration through the `loop`. After the first time through the loop, we - would not be able to call it again. - -When we use the `async fn` form with `.await`, Rust compiles it to something -smarter than the `loop` above, in conjunction with a *runtime* responsible for -executing that loop. - -> Note: Some languages, including Go, Kotlin, Erlang, and Swift, ship runtimes -> with the language. In Rust, there are many different runtimes, because a -> runtime might need to do very different things to support a high-throughput -> web server with dozens of CPU cores and terabytes of RAM than it would for a -> microcontroller with a single core and one gigabyte of RAM should do. - - - -The other thing to notice here is that futures in Rust are *lazy*. They do not -do anything until you explicitly ask them to—whether by calling `poll` or by -using `.await` to do so. This is different from the behavior we saw when using -`thread::spawn` in the previous chapter, and it is different from how many other -languages approach async. This allows Rust to avoid running async code unless it -is actually needed, and supports some of the memory safety features Rust brings -to async. (The details are beyond the scope of this book, but are well worth -digging into. In particular, see [Chapter 4: Pinning][pinning] in the official -[_Asynchronous Programming in Rust_][async-book] book.) +And in fact, when we use `.await`, Rust compiles it to something very similar to +that `loop`. However, if Rust compiled it to *only* that code, every `.await` +would block the computer from doing anything else—the opposite of what we were +going for! -### Running Async Code +> Note: It also wouldn’t compile, because it doesn’t actually satisfy the +> contract for a `Future`. In particular, `hello_async_fut` is not *pinned* with +> the `Pin` type and we did not pass along a `Context` argument. The details are +> beyond the scope of this book, but are well worth digging into. In particular, +> see [Chapter 4: Pinning][pinning] in the official [_Asynchronous Programming +> in Rust_][async-book] book. + + - +What is more, eventually we end up back in some non-async function. At that point, something needs to “translate” between the async +and sync worlds. That “something” is a *runtime*, a crate which handles the +top-level `poll()` call, scheduling and handing off between the different async +operations which may be in flight, and often providing async versions of +functionality like file I/O. -Going back to `main`, this explains why we cannot have an `async fn main`: what -would execute the async code? We need to pick a runtime and executor. +Now we can understand what is happening in Listing 17-XX. The `main` function is +not `async`—and it really cannot be: if it were, something would need to call +`poll()` on whatever `main` returned! Instead, we use the `trpl::block_on` +function, which polls the `Future` returned by `hello_async` until it returns +`Ready`. + +### Running Async Code Now, that’s a lot of work to just print a string, but we have laid some key foundations for working with async in Rust! Now that you know the basics of how @@ -284,3 +298,6 @@ futures work, and the [impl-trait]: ch10-02-traits.html#traits-as-parameters [pinning]: https://rust-lang.github.io/async-book/04_pinning/01_chapter.html [async-book]: https://rust-lang.github.io/async-book/ +[crate-source]: https://github.com/rust-lang/book/tree/main/packages/trpl + + diff --git a/src/ch17-02.md b/src/ch17-02.md index 9dfa186927..0eeb815d7b 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -6,11 +6,12 @@ runtimes in the Rust ecosystem. > ### The `futures` and `tokio` Crates > > Whenever you see code from the `trpl` crate throughout the rest of the -> chapter, it will be re-exporting code from the `futures` and [`tokio`][tokio] crates. +> chapter, it will be re-exporting code from the `futures` and [`tokio`][tokio] +> crates. > > - The `futures` crate is an official home for Rust experimentation for async > code, and is actually where the `Future` type was originally designed. -> +> > - Tokio is the most widely used async runtime in Rust today, especially (but > not only!) for web applications. There are other great options out there, > too, and they may be more suitable for your purposes. We are using Tokio @@ -38,4 +39,4 @@ async fn main() { [tokio]: https://tokio.rs -[crate-source]: TODO + From 2015179270b9c4de03c6a7a2bef6e1f12fdc10fe Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 10 May 2024 14:15:44 -0600 Subject: [PATCH 27/32] Ch. 17: wording and formatting in 17.01 --- src/ch17-01-tasks.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index b634c052f7..26237f901e 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -64,10 +64,10 @@ questions: ### Async functions -In Rust, `async fn` is equivalent to writing a function which returns a -future of the return type, using the `impl Trait` syntax we discussed back in -the [“Traits as Parameters”][impl-trait] section in Chapter 10. An `async` -block compiles to an anonymous struct which implements the `Future` trait. +In Rust, `async fn` is equivalent to writing a function which returns a future +of the return type, using the `impl Trait` syntax we discussed back in the +[“Traits as Parameters”][impl-trait] section in Chapter 10. An `async` block +compiles to an anonymous struct which implements the `Future` trait. That means these two are roughly equivalent: @@ -102,12 +102,6 @@ async. This allows Rust to avoid running async code unless it is actually needed. We will see why that is later on. For now, let’s start by awaiting the future returned by `hello_async` to actually have it run. -> Note: Rust’s `await` keyword goes *after* the expression you are awaiting—that -> is, it is a *postfix keyword*. This is different from what you might be used -> to if you have used async in languages like JavaScript or C#. Rust chose this -> because it makes chains of async and non-async methods much nicer to work -> with. As of now, `await` is the only postfix keyword in the language. - ```rust fn main() { @@ -115,6 +109,12 @@ fn main() { } ``` +> Note: Rust’s `await` keyword goes *after* the expression you are awaiting—that +> is, it is a *postfix keyword*. This is different from what you might be used +> to if you have used async in languages like JavaScript or C#. Rust chose this +> because it makes chains of async and non-async methods much nicer to work +> with. As of now, `await` is the only postfix keyword in the language. + Oh no! We have gone from a compiler warning to an actual error: ```text @@ -277,11 +277,11 @@ going for! -What is more, eventually we end up back in some non-async function. At that point, something needs to “translate” between the async -and sync worlds. That “something” is a *runtime*, a crate which handles the -top-level `poll()` call, scheduling and handing off between the different async -operations which may be in flight, and often providing async versions of -functionality like file I/O. +What is more, eventually we end up back in some non-async function. At that +point, something needs to “translate” between the async and sync worlds. That +“something” is a *runtime*, a crate which handles the top-level `poll()` call, +scheduling and handing off between the different async operations which may be +in flight, and often providing async versions of functionality like file I/O. Now we can understand what is happening in Listing 17-XX. The `main` function is not `async`—and it really cannot be: if it were, something would need to call @@ -293,7 +293,7 @@ function, which polls the `Future` returned by `hello_async` until it returns Now, that’s a lot of work to just print a string, but we have laid some key foundations for working with async in Rust! Now that you know the basics of how -futures work, and the +futures and runtimes work, we can see some of the things we can *do* with async. [impl-trait]: ch10-02-traits.html#traits-as-parameters [pinning]: https://rust-lang.github.io/async-book/04_pinning/01_chapter.html From d61b7360253352de2ce3841b4e4b3715e3badfc0 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 10 May 2024 16:27:15 -0600 Subject: [PATCH 28/32] Ch. 17: corrections/discussion on runtimes, mostly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tie off the discussion about needing a runtime so that it is clear what is needed in `main` and therefore why `main` cannot natively be `async` itself. - Correct the description of what `.await` compiles to. - Extend the note about the “under the hood” bits: mention generators so people know what to go looking for if they are curious. - Rewrite the existing introduction of the `#[async_main]` macro to lean on the material now covered in the previous chapter. --- src/ch17-01-tasks.md | 49 +++++++++++++++++++++++++------------------- src/ch17-02.md | 19 +++++++++-------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 26237f901e..2d747a0448 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -264,38 +264,45 @@ loop { ``` And in fact, when we use `.await`, Rust compiles it to something very similar to -that `loop`. However, if Rust compiled it to *only* that code, every `.await` -would block the computer from doing anything else—the opposite of what we were -going for! - -> Note: It also wouldn’t compile, because it doesn’t actually satisfy the -> contract for a `Future`. In particular, `hello_async_fut` is not *pinned* with -> the `Pin` type and we did not pass along a `Context` argument. The details are -> beyond the scope of this book, but are well worth digging into. In particular, -> see [Chapter 4: Pinning][pinning] in the official [_Asynchronous Programming -> in Rust_][async-book] book. - - - -What is more, eventually we end up back in some non-async function. At that -point, something needs to “translate” between the async and sync worlds. That -“something” is a *runtime*, a crate which handles the top-level `poll()` call, -scheduling and handing off between the different async operations which may be -in flight, and often providing async versions of functionality like file I/O. +that loop. If Rust compiled it to *exactly* that code, every `.await` would +block the computer from doing anything else—the opposite of what we were going +for! Instead, Rust internally makes sure that the loop can hand back control to +the the context of the code where which is awaiting this little bit of code. + +When we follow that chain far enough, eventually we end up back in some +non-async function. At that point, something needs to “translate” between the +async and sync worlds. That “something” is a *runtime*, a crate which handles +the top-level `poll()` call, scheduling and handing off between the different +async operations which may be in flight, and often providing async versions of +functionality like file I/O. Now we can understand what is happening in Listing 17-XX. The `main` function is not `async`—and it really cannot be: if it were, something would need to call `poll()` on whatever `main` returned! Instead, we use the `trpl::block_on` function, which polls the `Future` returned by `hello_async` until it returns -`Ready`. - -### Running Async Code +`Ready`. Every async program in Rust has at least one place where it sets up an +executor and executes code. + +> Note: Under the hood, Rust uses *generators* so that it can hand off control +> between different functions. These are an implementation detail, though, and +> you never have to think about it when writing Rust. +> +> The loop as written also wouldn’t compile, because it doesn’t actually satisfy +> the contract for a `Future`. In particular, `hello_async_fut` is not *pinned* +> with the `Pin` type and we did not pass along a `Context` argument. +> +> More details here are beyond the scope of this book, but are well worth +> digging into if you want to understand how things work “under the hood.” In +> particular, see [Chapter 2: Under the Hood: Executing Futures and +> Tasks][under-the-hood] and [Chapter 4: Pinning][pinning] in the official +> [_Asynchronous Programming in Rust_][async-book] book. Now, that’s a lot of work to just print a string, but we have laid some key foundations for working with async in Rust! Now that you know the basics of how futures and runtimes work, we can see some of the things we can *do* with async. [impl-trait]: ch10-02-traits.html#traits-as-parameters +[under-the-hood]: https://rust-lang.github.io/async-book/02_execution/01_chapter.html [pinning]: https://rust-lang.github.io/async-book/04_pinning/01_chapter.html [async-book]: https://rust-lang.github.io/async-book/ [crate-source]: https://github.com/rust-lang/book/tree/main/packages/trpl diff --git a/src/ch17-02.md b/src/ch17-02.md index 0eeb815d7b..0cf64d42a3 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -1,7 +1,9 @@ The executor from `futures` we used in the previous section is intentionally -quite limited. It works quite well, but if you are going to build a real-world -application, you will likely want to use the executor from one of the *other* -runtimes in the Rust ecosystem. +quite limited. It works well, but it requires you to set it up yourself, as we +did in the previous chapter, and it it is not tuned for any particular use case. +Accordingly, if you are going to build a real-world application, you will likely +want to use the executor from one of the *other* runtimes in the Rust +ecosystem. Once again, we will use code from the `trpl` crate to set things up. > ### The `futures` and `tokio` Crates > @@ -18,12 +20,10 @@ runtimes in the Rust ecosystem. > because it is the most widely-used runtime—not as a judgment call on whether > it is the *best* runtime! -Okay, now let’s start exploring. Going forward, we will use a new `async_main` -macro so that we can use `async fn` with a `main` function. Under the hood, this -little utility macro from Tokio sets up the Tokio runtime and wires up a call -kind of like we saw with `futures::executor::block_on` in the previous section. -Now we can use `async` blocks directly in `main` and not worry about wiring up -the runtime ourselves: +This time, it uses a macro, `async_main`, which allows us to use `async fn` with +a `main` function. The macro just rewrites the function to do something similar +to what we wrote by hand in the previous chapter, but lets us skip writing it +out by hand. Now we can write that `async` block and `.await` it in `main`: ```rust use trpl::async_main; @@ -36,6 +36,7 @@ async fn main() { } ``` +Okay, now let’s start exploring. [tokio]: https://tokio.rs From 4b6867e21da895ad6c3d4b907512ea804f2239fd Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 10 May 2024 16:28:39 -0600 Subject: [PATCH 29/32] Ch. 17: pull disconnected material out of 17.01 --- src/ch17-01-tasks.md | 20 -------------------- src/ch17-03-vs-threads.md | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 src/ch17-03-vs-threads.md diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 2d747a0448..48ee45f4a5 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -1,25 +1,5 @@ ## Futures and the Async Syntax -As we saw in the previous chapter, threads provide one approach to concurrency, -and they let us solve some of these issues. However, they also have some -tradeoffs. On many operating systems, they use a fair bit of memory for each -thread, and they come with some overhead for starting up and shutting down. -Threads are also only an option when your operating system and hardware support -them! While mainstream desktop and mobile operating systems have all had -threading for many years, many embedded operating systems, like those used on -some microcontrollers, do not. - -The async model provides a different—and ultimately complementary—set of -tradeoffs. In - - - -In the async model, concurrent operations do not require their own threads. -Instead, they can run on *tasks*. A task is a bit like a thread, but instead of -being managed by the operating system, it is managed by a runtime. - - - Like other languages with async, Rust uses the `async` and `await` keywords—though with some important differences, as we will see. Blocks and functions can be marked `async`, and you can wait on the result of an `async` diff --git a/src/ch17-03-vs-threads.md b/src/ch17-03-vs-threads.md new file mode 100644 index 0000000000..63f270522f --- /dev/null +++ b/src/ch17-03-vs-threads.md @@ -0,0 +1,23 @@ + + +As we saw in the previous chapter, threads provide one approach to concurrency, +and they let us solve some of these issues. However, they also have some +tradeoffs. On many operating systems, they use a fair bit of memory for each +thread, and they come with some overhead for starting up and shutting down. +Threads are also only an option when your operating system and hardware support +them! While mainstream desktop and mobile operating systems have all had +threading for many years, many embedded operating systems, like those used on +some microcontrollers, do not. + +The async model provides a different—and ultimately complementary—set of +tradeoffs. In the async model, concurrent operations do not require their own +threads. Instead, they can run on *tasks*. A task is a bit like a thread, but +instead of being managed by the operating system, it is managed by code that +lives at the level of libraries. + + From 41edfd4e1eaee93bbe097af2355d35d3a547a471 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Fri, 10 May 2024 16:41:42 -0600 Subject: [PATCH 30/32] Ch. 17: minor wording improvement --- src/ch17-01-tasks.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 48ee45f4a5..75411528dc 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -1,9 +1,10 @@ ## Futures and the Async Syntax Like other languages with async, Rust uses the `async` and `await` -keywords—though with some important differences, as we will see. Blocks and -functions can be marked `async`, and you can wait on the result of an `async` -function or block to resolve using the `await` keyword. +keywords—though with some important differences from how other languages do +things, as we will see. Blocks and functions can be marked `async`, and you can +wait on the result of an `async` function or block to resolve using the `await` +keyword. Let’s write our first async function, and call it: From 2625b8570dc35a6362dd4e19dc529a919f05b49b Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 14 May 2024 11:53:19 -0600 Subject: [PATCH 31/32] Ch. 17: use `Listing` for all existing listings --- src/ch17-01-tasks.md | 67 +++++++++++++++++++++++++++----------------- src/ch17-02.md | 4 +++ 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index 75411528dc..c045d1cd26 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -8,6 +8,8 @@ keyword. Let’s write our first async function, and call it: ++ ```rust fn main() { hello_async(); @@ -18,6 +20,8 @@ async fn hello_async() { } ``` + + If we compile and run this… nothing happens, and we get a compiler warning: ```console @@ -66,15 +70,10 @@ fn hello_async() -> impl Future { } ``` - - That explains why we got the `unused_must_use` warning: writing `async fn` meant -we were actually returning an anonymous `Future`. Just like with `Result`, Rust -will let us know if we don’t use a `Future`. It is often a mistake to ignore an -error in an operation your program performed. With a `Future`, it is even more -significant. The compiler also warned us that “futures do nothing unless you -`.await` or poll them”. That is, futures are *lazy*: they don’t do anything -until you ask them to. +we were actually returning an anonymous `Future`. The compiler will warn us that +“futures do nothing unless you `.await` or poll them”. That is, futures are +*lazy*: they don’t do anything until you ask them to. The compiler is telling us that ignoring a `Future` makes it completely useless! This is different from the behavior we saw when using `thread::spawn` in the @@ -83,18 +82,23 @@ async. This allows Rust to avoid running async code unless it is actually needed. We will see why that is later on. For now, let’s start by awaiting the future returned by `hello_async` to actually have it run. +> Note: Rust’s `await` keyword goes *after* the expression you are awaiting—that +> is, it is a _postfix keyword_. This is different from what you might be used +> to if you have used async in languages like JavaScript or C#. Rust chose this +> because it makes chains of async and non-async methods much nicer to work +> with. As of now, `await` is the only postfix keyword in the language. + ++ + ```rust fn main() { hello_async().await; } ``` -> Note: Rust’s `await` keyword goes *after* the expression you are awaiting—that -> is, it is a *postfix keyword*. This is different from what you might be used -> to if you have used async in languages like JavaScript or C#. Rust chose this -> because it makes chains of async and non-async methods much nicer to work -> with. As of now, `await` is the only postfix keyword in the language. + Oh no! We have gone from a compiler warning to an actual error: @@ -108,6 +112,8 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks | ^^^^^ only allowed inside `async` functions and blocks ``` + + This time, the compiler is informing us we cannot use `.await` in `main`, because `main` is not an `async` function. That is because async code needs a *runtime*: a Rust crate which manages the details of executing the asynchronous @@ -117,7 +123,7 @@ operations, and so on. Most languages which support async, including C#, JavaScript, Go, Kotlin, Erlang, and Swift, bundle a runtime with the language. At least for now, Rust does not. Instead, there are many different async runtimes available, each of -which makes different tradeoffs suitable to the use case they target. For +which makes different tradeoffs suitable to the use case they target. For example, a high-throughput web server with dozens of CPU cores and terabytes of RAM has very different different needs than a microcontroller with a single core, one gigabyte of RAM, and no ability to do heap allocations. @@ -138,10 +144,12 @@ For now, go ahead and add the `trpl` crate to your `hello-async` project: $ cargo add trpl ``` -In our `main` function, let’s wrap the call to `hello_async` with the +Then, in our `main` function, let’s wrap the call to `hello_async` with the `trpl::block_on` function, which takes in a `Future` and runs it until it completes. ++ ```rust fn main() { trpl::block_on(hello_async()); @@ -152,9 +160,12 @@ async fn hello_async() { } ``` + + When we run this, we get the behavior we might have expected initially: + ```console $ cargo run Compiling hello-async v0.1.0 (/Users/chris/dev/chriskrycho/async-trpl-fun/hello-async) @@ -214,6 +225,7 @@ Under the hood, when you call `.await`, Rust compiles that to code which calls `poll`, kind of like this: + ```rust match hello_async().poll() { Ready(_) => { @@ -230,6 +242,7 @@ the `Future` is still `Pending`? We need some way to try again. We would need to have something like this instead: + ```rust let hello_async_fut = hello_async(); loop { @@ -244,11 +257,12 @@ loop { } ``` -And in fact, when we use `.await`, Rust compiles it to something very similar to -that loop. If Rust compiled it to *exactly* that code, every `.await` would -block the computer from doing anything else—the opposite of what we were going -for! Instead, Rust internally makes sure that the loop can hand back control to -the the context of the code where which is awaiting this little bit of code. +When we use `.await`, Rust actually does compile it to something very similar to +that loop. If Rust compiled it to *exactly* that code, though, every `.await` +would block the computer from doing anything else—the opposite of what we were +going for! Instead, Rust internally makes sure that the loop can hand back +control to the the context of the code where which is awaiting this little bit +of code. When we follow that chain far enough, eventually we end up back in some non-async function. At that point, something needs to “translate” between the @@ -257,12 +271,13 @@ the top-level `poll()` call, scheduling and handing off between the different async operations which may be in flight, and often providing async versions of functionality like file I/O. -Now we can understand what is happening in Listing 17-XX. The `main` function is -not `async`—and it really cannot be: if it were, something would need to call -`poll()` on whatever `main` returned! Instead, we use the `trpl::block_on` -function, which polls the `Future` returned by `hello_async` until it returns -`Ready`. Every async program in Rust has at least one place where it sets up an -executor and executes code. +Now we can understand why the compiler was blocking us in Listing 17-2 (before +we added the `trpl::block_on` function). The `main` function is not `async`—and +it really cannot be: if it were, something would need to call `poll()` on +whatever `main` returned! Instead, we use the `trpl::block_on` function, which +polls the `Future` returned by `hello_async` until it returns `Ready`. Every +async program in Rust has at least one place where it sets up an executor and +executes code. > Note: Under the hood, Rust uses *generators* so that it can hand off control > between different functions. These are an implementation detail, though, and diff --git a/src/ch17-02.md b/src/ch17-02.md index 0cf64d42a3..116c5040f6 100644 --- a/src/ch17-02.md +++ b/src/ch17-02.md @@ -25,6 +25,8 @@ a `main` function. The macro just rewrites the function to do something similar to what we wrote by hand in the previous chapter, but lets us skip writing it out by hand. Now we can write that `async` block and `.await` it in `main`: ++ ```rust use trpl::async_main; @@ -36,6 +38,8 @@ async fn main() { } ``` + + Okay, now let’s start exploring. From 14febcc6c5a9cba224fd900066ed4e235394f2b3 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 14 May 2024 12:25:48 -0600 Subject: [PATCH 32/32] Ch. 17: eliminate duplicate runtime description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep both references, but make the first one a simple definition, and the second one the “ah, now we can make sense of that definition”. --- src/ch17-01-tasks.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/ch17-01-tasks.md b/src/ch17-01-tasks.md index c045d1cd26..7ce057fcd9 100644 --- a/src/ch17-01-tasks.md +++ b/src/ch17-01-tasks.md @@ -112,13 +112,10 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks | ^^^^^ only allowed inside `async` functions and blocks ``` - - This time, the compiler is informing us we cannot use `.await` in `main`, because `main` is not an `async` function. That is because async code needs a -*runtime*: a Rust crate which manages the details of executing the asynchronous -code, including whether or not to use threads for it, scheduling different async -operations, and so on. +*runtime*: a Rust crate which manages the details of executing asynchronous +code. Most languages which support async, including C#, JavaScript, Go, Kotlin, Erlang, and Swift, bundle a runtime with the language. At least for now, Rust @@ -266,10 +263,10 @@ of code. When we follow that chain far enough, eventually we end up back in some non-async function. At that point, something needs to “translate” between the -async and sync worlds. That “something” is a *runtime*, a crate which handles -the top-level `poll()` call, scheduling and handing off between the different -async operations which may be in flight, and often providing async versions of -functionality like file I/O. +async and sync worlds. That “something” is the runtime! Whatever runtime you use +is what handles the top-level `poll()` call, scheduling and handing off between +the different async operations which may be in flight, and often also providing +async versions of functionality like file I/O. Now we can understand why the compiler was blocking us in Listing 17-2 (before we added the `trpl::block_on` function). The `main` function is not `async`—and