From 0ec7296ff72026f0880b7d2fe329996294c70cdd Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 24 Apr 2024 15:32:03 -0600 Subject: [PATCH 01/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] =?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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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