├── .github └── workflows │ └── gh-rfcs-mdbook.yml ├── .gitignore ├── .travis.yml ├── 0000-template.md ├── CONTRIBUTING.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── book.toml ├── compiler_changes.md ├── generate-book.sh ├── lang_changes.md ├── libs_changes.md ├── resources └── project-group-workflow.svg ├── style-guide ├── README.md ├── advice.md ├── cargo.md ├── expressions.md ├── items.md ├── principles.md ├── statements.md └── types.md └── text ├── 0049-match-arm-attributes.md ├── 0198-slice-notation.md ├── 1548-global-asm.md ├── 1598-generic_associated_types.md ├── 2000-const-generics.md ├── 2342-const-control-flow.md ├── 2394-async_await.md ├── 2495-min-rust-version.md ├── 2592-futures.md ├── 2789-sparse-index.md ├── 2843-llvm-asm.md ├── 3128-io-safety.md ├── 3151-scoped-threads.md ├── 3392-leadership-council.md └── 3392-leadership-council ├── alternatives.md ├── initial-work-of-the-council.md ├── motivation.md └── non-goals.md /.github/workflows/gh-rfcs-mdbook.yml: -------------------------------------------------------------------------------- 1 | name: RFC Book 2 | 3 | on: 4 | push: 5 | branches: 6 | - zh_tw 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | 15 | # Install Rust package manager 16 | # 17 | - name: Install cargo 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: stable 21 | profile: minimal 22 | 23 | # Cache installation assets 24 | # 25 | - name: Cache cargo registry 26 | uses: actions/cache@v1 27 | with: 28 | path: ~/.cargo/registry 29 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 30 | - name: Cache cargo index 31 | uses: actions/cache@v1 32 | with: 33 | path: ~/.cargo/git 34 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 35 | - name: Cache cargo build 36 | uses: actions/cache@v1 37 | with: 38 | path: target 39 | key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} 40 | 41 | # Install mdbook and requirements 42 | # 43 | - name: Install mdbook 44 | uses: actions-rs/cargo@v1 45 | with: 46 | command: install 47 | args: mdbook 48 | 49 | - name: Generate summary 50 | run: ./generate-book.sh 51 | 52 | # HTML publication as Github Page 53 | # 54 | - name: Publish HTML 55 | uses: peaceiris/actions-gh-pages@v3 56 | with: 57 | github_token: ${{ secrets.GITHUB_TOKEN }} 58 | publish_dir: ./book 59 | 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .idea 3 | book/ 4 | src/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | branches: 3 | only: 4 | - master 5 | 6 | cache: 7 | directories: 8 | - ~/.cargo 9 | - target 10 | before_cache: 11 | - cargo install cargo-cache --no-default-features --features ci-autoclean cargo-cache 12 | - cargo cache 13 | install: 14 | - export CARGO_TARGET_DIR=$TRAVIS_BUILD_DIR/target 15 | - cargo install mdbook 16 | script: 17 | - ./generate-book.sh 18 | 19 | env: 20 | RUSTINFRA_DEPLOY_DIR: book 21 | import: 22 | - rust-lang/simpleinfra:travis-configs/static-websites.yml 23 | -------------------------------------------------------------------------------- /0000-template.md: -------------------------------------------------------------------------------- 1 | - Feature Name: (fill me in with a unique ident, `my_awesome_feature`) 2 | - Start Date: (fill me in with today's date, YYYY-MM-DD) 3 | - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) 4 | - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) 5 | 6 | # Summary 7 | [summary]: #summary 8 | 9 | One paragraph explanation of the feature. 10 | 11 | # Motivation 12 | [motivation]: #motivation 13 | 14 | Why are we doing this? What use cases does it support? What is the expected outcome? 15 | 16 | # Guide-level explanation 17 | [guide-level-explanation]: #guide-level-explanation 18 | 19 | Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: 20 | 21 | - Introducing new named concepts. 22 | - Explaining the feature largely in terms of examples. 23 | - Explaining how Rust programmers should *think* about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. 24 | - If applicable, provide sample error messages, deprecation warnings, or migration guidance. 25 | - If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers. 26 | 27 | For implementation-oriented RFCs (e.g. for compiler internals), this section should focus on how compiler contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. 28 | 29 | # Reference-level explanation 30 | [reference-level-explanation]: #reference-level-explanation 31 | 32 | This is the technical portion of the RFC. Explain the design in sufficient detail that: 33 | 34 | - Its interaction with other features is clear. 35 | - It is reasonably clear how the feature would be implemented. 36 | - Corner cases are dissected by example. 37 | 38 | The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work. 39 | 40 | # Drawbacks 41 | [drawbacks]: #drawbacks 42 | 43 | Why should we *not* do this? 44 | 45 | # Rationale and alternatives 46 | [rationale-and-alternatives]: #rationale-and-alternatives 47 | 48 | - Why is this design the best in the space of possible designs? 49 | - What other designs have been considered and what is the rationale for not choosing them? 50 | - What is the impact of not doing this? 51 | 52 | # Prior art 53 | [prior-art]: #prior-art 54 | 55 | Discuss prior art, both the good and the bad, in relation to this proposal. 56 | A few examples of what this can include are: 57 | 58 | - For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had? 59 | - For community proposals: Is this done by some other community and what were their experiences with it? 60 | - For other teams: What lessons can we learn from what other communities have done here? 61 | - Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. 62 | 63 | This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. 64 | If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages. 65 | 66 | Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC. 67 | Please also take into consideration that rust sometimes intentionally diverges from common language features. 68 | 69 | # Unresolved questions 70 | [unresolved-questions]: #unresolved-questions 71 | 72 | - What parts of the design do you expect to resolve through the RFC process before this gets merged? 73 | - What parts of the design do you expect to resolve through the implementation of this feature before stabilization? 74 | - What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? 75 | 76 | # Future possibilities 77 | [future-possibilities]: #future-possibilities 78 | 79 | Think about what the natural extension and evolution of your proposal would 80 | be and how it would affect the language and project as a whole in a holistic 81 | way. Try to use this section as a tool to more fully consider all possible 82 | interactions with the project and language in your proposal. 83 | Also consider how the this all fits into the roadmap for the project 84 | and of the relevant sub-team. 85 | 86 | This is also a good place to "dump ideas", if they are out of scope for the 87 | RFC you are writing but otherwise related. 88 | 89 | If you have tried and cannot think of any future possibilities, 90 | you may simply state that you cannot think of anything. 91 | 92 | Note that having something written down in the future-possibilities section 93 | is not a reason to accept the current or a future RFC; such notes should be 94 | in the section on motivation or rationale in this or subsequent RFCs. 95 | The section merely provides additional information. 96 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 我們歡迎任何協助,非常感謝你願意一起支援這個社群! 4 | 5 | ## 授權條款 6 | 7 | 此專案的授權條款和其他 Rust 專案一樣都是 MIT/Apache2。你可以在 `LICENSE-*` 檔案中閱讀條款全文 8 | 9 | ## 行為準則 10 | 11 | Rust 專案有一個 [行為準則](http://rust-lang.org/policies/code-of-conduct)會囊括其下專案,這當然包含此專案。請遵守這些準則! 12 | 13 | ## 翻譯流程 14 | 15 | 我們的 [open pull requests][pulls] 接受任何翻譯或修正文章的 PR。在想開始翻譯一篇 RFC 前請先開一個 issue,如果你有希望被翻譯的也一樣歡迎開。翻譯完文章後請在每一篇文底加上以下以下資訊: 16 | 17 | ``` 18 | translators: [Firstname Lastname ] 19 | commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/...) 20 | updated: YYYY-MMM-DD 21 | ``` 22 | 23 | [pulls]: https://github.com/rust-tw/rfcs/pulls 24 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust RFCs - [Active RFC List](https://rfcbot.rs/) 2 | 3 | [Rust RFCs]: #rust-rfcs 4 | 5 | **注意**:這是官方 rust rfcs 的 fork。如果你有興趣協助翻譯的話,歡迎前往 [CONTRIBUTING.md](CONTRIBUTING.md) 瞭解更多細節。 6 | 7 | **Note**: This is a fork of official rust rfcs. If you are interested in help translating, please have a look at [CONTRIBUTING.md](CONTRIBUTING.md). 8 | 9 | Many changes, including bug fixes and documentation improvements can be 10 | implemented and reviewed via the normal GitHub pull request workflow. 11 | 12 | Some changes though are "substantial", and we ask that these be put through a 13 | bit of a design process and produce a consensus among the Rust community and 14 | the [sub-team]s. 15 | 16 | The "RFC" (request for comments) process is intended to provide a consistent 17 | and controlled path for new features to enter the language and standard 18 | libraries, so that all stakeholders can be confident about the direction the 19 | language is evolving in. 20 | 21 | 22 | ## Table of Contents 23 | [Table of Contents]: #table-of-contents 24 | 25 | - [Opening](#rust-rfcs) 26 | - [Table of Contents] 27 | - [When you need to follow this process] 28 | - [Before creating an RFC] 29 | - [What the process is] 30 | - [The RFC life-cycle] 31 | - [Reviewing RFCs] 32 | - [Implementing an RFC] 33 | - [RFC Postponement] 34 | - [Help this is all too informal!] 35 | - [License] 36 | 37 | 38 | ## When you need to follow this process 39 | [When you need to follow this process]: #when-you-need-to-follow-this-process 40 | 41 | You need to follow this process if you intend to make "substantial" changes to 42 | Rust, Cargo, Crates.io, or the RFC process itself. What constitutes a 43 | "substantial" change is evolving based on community norms and varies depending 44 | on what part of the ecosystem you are proposing to change, but may include the 45 | following. 46 | 47 | - Any semantic or syntactic change to the language that is not a bugfix. 48 | - Removing language features, including those that are feature-gated. 49 | - Changes to the interface between the compiler and libraries, including lang 50 | items and intrinsics. 51 | - Additions to `std`. 52 | 53 | Some changes do not require an RFC: 54 | 55 | - Rephrasing, reorganizing, refactoring, or otherwise "changing shape does 56 | not change meaning". 57 | - Additions that strictly improve objective, numerical quality criteria 58 | (warning removal, speedup, better platform coverage, more parallelism, trap 59 | more errors, etc.) 60 | - Additions only likely to be _noticed by_ other developers-of-rust, 61 | invisible to users-of-rust. 62 | 63 | If you submit a pull request to implement a new feature without going through 64 | the RFC process, it may be closed with a polite request to submit an RFC first. 65 | 66 | 67 | ### Sub-team specific guidelines 68 | [Sub-team specific guidelines]: #sub-team-specific-guidelines 69 | 70 | For more details on when an RFC is required for the following areas, please see 71 | the Rust community's [sub-team] specific guidelines for: 72 | 73 | - [language changes](lang_changes.md), 74 | - [library changes](libs_changes.md), 75 | - [compiler changes](compiler_changes.md). 76 | 77 | 78 | ## Before creating an RFC 79 | [Before creating an RFC]: #before-creating-an-rfc 80 | 81 | A hastily-proposed RFC can hurt its chances of acceptance. Low quality 82 | proposals, proposals for previously-rejected features, or those that don't fit 83 | into the near-term roadmap, may be quickly rejected, which can be demotivating 84 | for the unprepared contributor. Laying some groundwork ahead of the RFC can 85 | make the process smoother. 86 | 87 | Although there is no single way to prepare for submitting an RFC, it is 88 | generally a good idea to pursue feedback from other project developers 89 | beforehand, to ascertain that the RFC may be desirable; having a consistent 90 | impact on the project requires concerted effort toward consensus-building. 91 | 92 | The most common preparations for writing and submitting an RFC include talking 93 | the idea over on our [official Discord server], discussing the topic on our 94 | [developer discussion forum], and occasionally posting "pre-RFCs" on the 95 | developer forum. You may file issues on this repo for discussion, but these are 96 | not actively looked at by the teams. 97 | 98 | As a rule of thumb, receiving encouraging feedback from long-standing project 99 | developers, and particularly members of the relevant [sub-team] is a good 100 | indication that the RFC is worth pursuing. 101 | 102 | 103 | ## What the process is 104 | [What the process is]: #what-the-process-is 105 | 106 | In short, to get a major feature added to Rust, one must first get the RFC 107 | merged into the RFC repository as a markdown file. At that point the RFC is 108 | "active" and may be implemented with the goal of eventual inclusion into Rust. 109 | 110 | - Fork the RFC repo [RFC repository] 111 | - Copy `0000-template.md` to `text/0000-my-feature.md` (where "my-feature" is 112 | descriptive). Don't assign an RFC number yet; This is going to be the PR 113 | number and we'll rename the file accordingly if the RFC is accepted. 114 | - Fill in the RFC. Put care into the details: RFCs that do not present 115 | convincing motivation, demonstrate lack of understanding of the design's 116 | impact, or are disingenuous about the drawbacks or alternatives tend to 117 | be poorly-received. 118 | - Submit a pull request. As a pull request the RFC will receive design 119 | feedback from the larger community, and the author should be prepared to 120 | revise it in response. 121 | - Each pull request will be labeled with the most relevant [sub-team], which 122 | will lead to its being triaged by that team in a future meeting and assigned 123 | to a member of the subteam. 124 | - Build consensus and integrate feedback. RFCs that have broad support are 125 | much more likely to make progress than those that don't receive any 126 | comments. Feel free to reach out to the RFC assignee in particular to get 127 | help identifying stakeholders and obstacles. 128 | - The sub-team will discuss the RFC pull request, as much as possible in the 129 | comment thread of the pull request itself. Offline discussion will be 130 | summarized on the pull request comment thread. 131 | - RFCs rarely go through this process unchanged, especially as alternatives 132 | and drawbacks are shown. You can make edits, big and small, to the RFC to 133 | clarify or change the design, but make changes as new commits to the pull 134 | request, and leave a comment on the pull request explaining your changes. 135 | Specifically, do not squash or rebase commits after they are visible on the 136 | pull request. 137 | - At some point, a member of the subteam will propose a "motion for final 138 | comment period" (FCP), along with a *disposition* for the RFC (merge, close, 139 | or postpone). 140 | - This step is taken when enough of the tradeoffs have been discussed that 141 | the subteam is in a position to make a decision. That does not require 142 | consensus amongst all participants in the RFC thread (which is usually 143 | impossible). However, the argument supporting the disposition on the RFC 144 | needs to have already been clearly articulated, and there should not be a 145 | strong consensus *against* that position outside of the subteam. Subteam 146 | members use their best judgment in taking this step, and the FCP itself 147 | ensures there is ample time and notification for stakeholders to push back 148 | if it is made prematurely. 149 | - For RFCs with lengthy discussion, the motion to FCP is usually preceded by 150 | a *summary comment* trying to lay out the current state of the discussion 151 | and major tradeoffs/points of disagreement. 152 | - Before actually entering FCP, *all* members of the subteam must sign off; 153 | this is often the point at which many subteam members first review the RFC 154 | in full depth. 155 | - The FCP lasts ten calendar days, so that it is open for at least 5 business 156 | days. It is also advertised widely, 157 | e.g. in [This Week in Rust](https://this-week-in-rust.org/). This way all 158 | stakeholders have a chance to lodge any final objections before a decision 159 | is reached. 160 | - In most cases, the FCP period is quiet, and the RFC is either merged or 161 | closed. However, sometimes substantial new arguments or ideas are raised, 162 | the FCP is canceled, and the RFC goes back into development mode. 163 | 164 | ## The RFC life-cycle 165 | [The RFC life-cycle]: #the-rfc-life-cycle 166 | 167 | Once an RFC becomes "active" then authors may implement it and submit the 168 | feature as a pull request to the Rust repo. Being "active" is not a rubber 169 | stamp, and in particular still does not mean the feature will ultimately be 170 | merged; it does mean that in principle all the major stakeholders have agreed 171 | to the feature and are amenable to merging it. 172 | 173 | Furthermore, the fact that a given RFC has been accepted and is "active" 174 | implies nothing about what priority is assigned to its implementation, nor does 175 | it imply anything about whether a Rust developer has been assigned the task of 176 | implementing the feature. While it is not *necessary* that the author of the 177 | RFC also write the implementation, it is by far the most effective way to see 178 | an RFC through to completion: authors should not expect that other project 179 | developers will take on responsibility for implementing their accepted feature. 180 | 181 | Modifications to "active" RFCs can be done in follow-up pull requests. We 182 | strive to write each RFC in a manner that it will reflect the final design of 183 | the feature; but the nature of the process means that we cannot expect every 184 | merged RFC to actually reflect what the end result will be at the time of the 185 | next major release. 186 | 187 | In general, once accepted, RFCs should not be substantially changed. Only very 188 | minor changes should be submitted as amendments. More substantial changes 189 | should be new RFCs, with a note added to the original RFC. Exactly what counts 190 | as a "very minor change" is up to the sub-team to decide; check 191 | [Sub-team specific guidelines] for more details. 192 | 193 | 194 | ## Reviewing RFCs 195 | [Reviewing RFCs]: #reviewing-rfcs 196 | 197 | While the RFC pull request is up, the sub-team may schedule meetings with the 198 | author and/or relevant stakeholders to discuss the issues in greater detail, 199 | and in some cases the topic may be discussed at a sub-team meeting. In either 200 | case a summary from the meeting will be posted back to the RFC pull request. 201 | 202 | A sub-team makes final decisions about RFCs after the benefits and drawbacks 203 | are well understood. These decisions can be made at any time, but the sub-team 204 | will regularly issue decisions. When a decision is made, the RFC pull request 205 | will either be merged or closed. In either case, if the reasoning is not clear 206 | from the discussion in thread, the sub-team will add a comment describing the 207 | rationale for the decision. 208 | 209 | 210 | ## Implementing an RFC 211 | [Implementing an RFC]: #implementing-an-rfc 212 | 213 | Some accepted RFCs represent vital features that need to be implemented right 214 | away. Other accepted RFCs can represent features that can wait until some 215 | arbitrary developer feels like doing the work. Every accepted RFC has an 216 | associated issue tracking its implementation in the Rust repository; thus that 217 | associated issue can be assigned a priority via the triage process that the 218 | team uses for all issues in the Rust repository. 219 | 220 | The author of an RFC is not obligated to implement it. Of course, the RFC 221 | author (like any other developer) is welcome to post an implementation for 222 | review after the RFC has been accepted. 223 | 224 | If you are interested in working on the implementation for an "active" RFC, but 225 | cannot determine if someone else is already working on it, feel free to ask 226 | (e.g. by leaving a comment on the associated issue). 227 | 228 | 229 | ## RFC Postponement 230 | [RFC Postponement]: #rfc-postponement 231 | 232 | Some RFC pull requests are tagged with the "postponed" label when they are 233 | closed (as part of the rejection process). An RFC closed with "postponed" is 234 | marked as such because we want neither to think about evaluating the proposal 235 | nor about implementing the described feature until some time in the future, and 236 | we believe that we can afford to wait until then to do so. Historically, 237 | "postponed" was used to postpone features until after 1.0. Postponed pull 238 | requests may be re-opened when the time is right. We don't have any formal 239 | process for that, you should ask members of the relevant sub-team. 240 | 241 | Usually an RFC pull request marked as "postponed" has already passed an 242 | informal first round of evaluation, namely the round of "do we think we would 243 | ever possibly consider making this change, as outlined in the RFC pull request, 244 | or some semi-obvious variation of it." (When the answer to the latter question 245 | is "no", then the appropriate response is to close the RFC, not postpone it.) 246 | 247 | 248 | ### Help this is all too informal! 249 | [Help this is all too informal!]: #help-this-is-all-too-informal 250 | 251 | The process is intended to be as lightweight as reasonable for the present 252 | circumstances. As usual, we are trying to let the process be driven by 253 | consensus and community norms, not impose more structure than necessary. 254 | 255 | 256 | [official Discord server]: https://discord.gg/rust-lang 257 | [developer discussion forum]: http://internals.rust-lang.org/ 258 | [RFC repository]: http://github.com/rust-lang/rfcs 259 | [sub-team]: http://www.rust-lang.org/team.html 260 | 261 | ## License 262 | [License]: #license 263 | 264 | This repository is currently in the process of being licensed under either of: 265 | 266 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 267 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 268 | 269 | at your option. Some parts of the repository are already licensed according to those terms. For more see [RFC 2044](https://github.com/rust-lang/rfcs/pull/2044) and its [tracking issue](https://github.com/rust-lang/rust/issues/43461). 270 | 271 | ### Contributions 272 | 273 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 274 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "The Rust RFC Book" 3 | 4 | [output.html] 5 | no-section-label = true 6 | -------------------------------------------------------------------------------- /compiler_changes.md: -------------------------------------------------------------------------------- 1 | # RFC policy - the compiler 2 | 3 | We have not previously had an RFC system for compiler changes, so policy here is 4 | likely to change as we get the hang of things. We don't want to slow down most 5 | compiler development, but on the other hand we do want to do more design work 6 | ahead of time on large additions and refactorings. 7 | 8 | Compiler RFCs will be managed by the compiler sub-team, and tagged `T-compiler`. 9 | The compiler sub-team will do an initial triage of new PRs within a week of 10 | submission. The result of triage will either be that the PR is assigned to a 11 | member of the sub-team for shepherding, the PR is closed because the sub-team 12 | believe it should be done without an RFC, or closed because the sub-team feel it 13 | should clearly not be done and further discussion is not necessary. We'll follow 14 | the standard procedure for shepherding, final comment period, etc. 15 | 16 | Where there is significant design work for the implementation of a language 17 | feature, the preferred workflow is to submit two RFCs - one for the language 18 | design and one for the implementation design. The implementation RFC may be 19 | submitted later if there is scope for large changes to the language RFC. 20 | 21 | 22 | ## Changes which need an RFC 23 | 24 | * New lints (these fall under the lang team) 25 | * Large refactorings or redesigns of the compiler 26 | * Changing the API presented to syntax extensions or other compiler plugins in 27 | non-trivial ways 28 | * Adding, removing, or changing a stable compiler flag 29 | * The implementation of new language features where there is significant change 30 | or addition to the compiler. There is obviously some room for interpretation 31 | about what consitutes a "significant" change and how much detail the 32 | implementation RFC needs. For guidance, [associated items](text/0195-associated-items.md) 33 | and [UFCS](text/0132-ufcs.md) would clearly need an implementation RFC, 34 | [type ascription](text/0803-type-ascription.md) and 35 | [lifetime elision](text/0141-lifetime-elision.md) would not. 36 | * Any other change which causes backwards incompatible changes to stable 37 | behaviour of the compiler, language, or libraries 38 | 39 | 40 | ## Changes which don't need an RFC 41 | 42 | * Bug fixes, improved error messages, etc. 43 | * Minor refactoring/tidying up 44 | * Implmenting language features which have an accepted RFC, where the 45 | implementation does not significantly change the compiler or require 46 | significant new design work 47 | * Adding unstable API for tools (note that all compiler API is currently unstable) 48 | * Adding, removing, or changing an unstable compiler flag (if the compiler flag 49 | is widely used there should be at least some discussion on discuss, or an RFC 50 | in some cases) 51 | 52 | If in doubt it is probably best to just announce the change you want to make to 53 | the compiler subteam on discuss or IRC, and see if anyone feels it needs an RFC. 54 | -------------------------------------------------------------------------------- /generate-book.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ ! -d src ]; then 6 | mkdir src 7 | fi 8 | 9 | printf '[Introduction](introduction.md)\n\n' > src/SUMMARY.md 10 | 11 | find ./text ! -type d -print0 | xargs -0 -I {} ln -frs {} -t src/ 12 | 13 | find ./text ! -type d -name '*.md' -print0 \ 14 | | sort -z \ 15 | | while read -r -d '' file; 16 | do 17 | printf -- '- [%s](%s)\n' "$(basename "$file" ".md")" "$(basename "$file")" 18 | done >> src/SUMMARY.md 19 | 20 | printf '\n[Contributing](CONTRIBUTING.md)\n\n' >> src/SUMMARY.md 21 | 22 | ln -frs README.md src/introduction.md 23 | ln -frs CONTRIBUTING.md src/CONTRIBUTING.md 24 | 25 | mdbook build 26 | -------------------------------------------------------------------------------- /lang_changes.md: -------------------------------------------------------------------------------- 1 | # RFC policy - language design 2 | 3 | Pretty much every change to the language needs an RFC. Note that new 4 | lints (or major changes to an existing lint) are considered changes to 5 | the language. 6 | 7 | Language RFCs are managed by the language sub-team, and tagged `T-lang`. The 8 | language sub-team will do an initial triage of new PRs within a week of 9 | submission. The result of triage will either be that the PR is assigned to a 10 | member of the sub-team for shepherding, the PR is closed as postponed because 11 | the subteam believe it might be a good idea, but is not currently aligned with 12 | Rust's priorities, or the PR is closed because the sub-team feel it should 13 | clearly not be done and further discussion is not necessary. In the latter two 14 | cases, the sub-team will give a detailed explanation. We'll follow the standard 15 | procedure for shepherding, final comment period, etc. 16 | 17 | 18 | ## Amendments 19 | 20 | Sometimes in the implementation of an RFC, changes are required. In general 21 | these don't require an RFC as long as they are very minor and in the spirit of 22 | the accepted RFC (essentially bug fixes). In this case implementers should 23 | submit an RFC PR which amends the accepted RFC with the new details. Although 24 | the RFC repository is not intended as a reference manual, it is preferred that 25 | RFCs do reflect what was actually implemented. Amendment RFCs will go through 26 | the same process as regular RFCs, but should be less controversial and thus 27 | should move more quickly. 28 | 29 | When a change is more dramatic, it is better to create a new RFC. The RFC should 30 | be standalone and reference the original, rather than modifying the existing 31 | RFC. You should add a comment to the original RFC with referencing the new RFC 32 | as part of the PR. 33 | 34 | Obviously there is some scope for judgment here. As a guideline, if a change 35 | affects more than one part of the RFC (i.e., is a non-local change), affects the 36 | applicability of the RFC to its motivating use cases, or there are multiple 37 | possible new solutions, then the feature is probably not 'minor' and should get 38 | a new RFC. 39 | -------------------------------------------------------------------------------- /libs_changes.md: -------------------------------------------------------------------------------- 1 | # RFC guidelines - libraries sub-team 2 | 3 | # Motivation 4 | 5 | * RFCs are heavyweight: 6 | * RFCs generally take at minimum 2 weeks from posting to land. In 7 | practice it can be more on the order of months for particularly 8 | controversial changes. 9 | * RFCs are a lot of effort to write; especially for non-native speakers or 10 | for members of the community whose strengths are more technical than literary. 11 | * RFCs may involve pre-RFCs and several rewrites to accommodate feedback. 12 | * RFCs require a dedicated shepherd to herd the community and author towards 13 | consensus. 14 | * RFCs require review from a majority of the subteam, as well as an official 15 | vote. 16 | * RFCs can't be downgraded based on their complexity. Full process always applies. 17 | Easy RFCs may certainly land faster, though. 18 | * RFCs can be very abstract and hard to grok the consequences of (no implementation). 19 | 20 | * PRs are low *overhead* but potentially expensive nonetheless: 21 | * Easy PRs can get insta-merged by any rust-lang contributor. 22 | * Harder PRs can be easily escalated. You can ping subject-matter experts for second 23 | opinions. Ping the whole team! 24 | * Easier to grok the full consequences. Lots of tests and Crater to save the day. 25 | * PRs can be accepted optimistically with bors, buildbot, and the trains to guard 26 | us from major mistakes making it into stable. The size of the nightly community 27 | at this point in time can still mean major community breakage regardless of trains, 28 | however. 29 | * HOWEVER: Big PRs can be a lot of work to make only to have that work rejected for 30 | details that could have been hashed out first. 31 | 32 | * RFCs are *only* meaningful if a significant and diverse portion of the 33 | community actively participates in them. The official teams are not 34 | sufficiently diverse to establish meaningful community consensus by agreeing 35 | amongst themselves. 36 | 37 | * If there are *tons* of RFCs -- especially trivial ones -- people are less 38 | likely to engage with them. Official team members are super busy. Domain experts 39 | and industry professionals are super busy *and* have no responsibility to engage 40 | in RFCs. Since these are *exactly* the most important people to get involved in 41 | the RFC process, it is important that we be maximally friendly towards their 42 | needs. 43 | 44 | 45 | # Is an RFC required? 46 | 47 | The overarching philosophy is: *do whatever is easiest*. If an RFC 48 | would be less work than an implementation, that's a good sign that an RFC is 49 | necessary. That said, if you anticipate controversy, you might want to short-circuit 50 | straight to an RFC. For instance new APIs almost certainly merit an RFC. Especially 51 | as `std` has become more conservative in favour of the much more agile cargoverse. 52 | 53 | * **Submit a PR** if the change is a: 54 | * Bugfix 55 | * Docfix 56 | * Obvious API hole patch, such as adding an API from one type to a symmetric type. 57 | e.g. `Vec -> Box<[T]>` clearly motivates adding `String -> Box` 58 | * Minor tweak to an unstable API (renaming, generalizing) 59 | * Implementing an "obvious" trait like Clone/Debug/etc 60 | * **Submit an RFC** if the change is a: 61 | * New API 62 | * Semantic Change to a stable API 63 | * Generalization of a stable API (e.g. how we added Pattern or Borrow) 64 | * Deprecation of a stable API 65 | * Nontrivial trait impl (because all trait impls are insta-stable) 66 | * **Do the easier thing** if uncertain. (choosing a path is not final) 67 | 68 | 69 | # Non-RFC process 70 | 71 | * A (non-RFC) PR is likely to be **closed** if clearly not acceptable: 72 | * Disproportionate breaking change (small inference breakage may be acceptable) 73 | * Unsound 74 | * Doesn't fit our general design philosophy around the problem 75 | * Better as a crate 76 | * Too marginal for std 77 | * Significant implementation problems 78 | 79 | * A PR may also be closed because an RFC is approriate. 80 | 81 | * A (non-RFC) PR may be **merged as unstable**. In this case, the feature 82 | should have a fresh feature gate and an associated tracking issue for 83 | stabilisation. Note that trait impls and docs are insta-stable and thus have no 84 | tracking issue. This may imply requiring a higher level of scrutiny for such 85 | changes. 86 | 87 | However, an accepted RFC is not a rubber-stamp for merging an implementation PR. 88 | Nor must an implementation PR perfectly match the RFC text. Implementation details 89 | may merit deviations, though obviously they should be justified. The RFC may be 90 | amended if deviations are substantial, but are not generally necessary. RFCs should 91 | favour immutability. The RFC + Issue + PR should form a total explanation of the 92 | current implementation. 93 | 94 | * Once something has been merged as unstable, a shepherd should be assigned 95 | to promote and obtain feedback on the design. 96 | 97 | * Every time a release cycle ends, the libs teams assesses the current unstable 98 | APIs and selects some number of them for potential stabilization during the 99 | next cycle. These are announced for FCP at the beginning of the cycle, and 100 | (possibly) stabilized just before the beta is cut. 101 | 102 | * After the final comment period, an API should ideally take one of two paths: 103 | * **Stabilize** if the change is desired, and consensus is reached 104 | * **Deprecate** is the change is undesired, and consensus is reached 105 | * **Extend the FCP** is the change cannot meet consensus 106 | * If consensus *still* can't be reached, consider requiring a new RFC or 107 | just deprecating as "too controversial for std". 108 | 109 | * If any problems are found with a newly stabilized API during its beta period, 110 | *strongly* favour reverting stability in order to prevent stabilizing a bad 111 | API. Due to the speed of the trains, this is not a serious delay (~2-3 months 112 | if it's not a major problem). 113 | 114 | 115 | -------------------------------------------------------------------------------- /resources/project-group-workflow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 Initial work- Discuss problem area- Concrete charter- Notes on solutions- Find liaison2 Decision- Meeting + notes. . . or RFC3 Create - Zulip/Discord- Github repo + team- (etc)6 Create blog post- Overview of decisions,-  RFCs, other output- Thoughts on process,difficulties encountered
No
No
Consensus
to create?
Consensus...
Yes
Yes
Not created7 Archive- Archive repository- Archive Zulip/Discordany other related items4 Group works(Can include  follow-up RFCs with finalized designs)5 Active work stops- No one has time- Higher priority work arose- Blocking issue - Work sufficiently complete- Idea not good enough
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /style-guide/README.md: -------------------------------------------------------------------------------- 1 | # Rust Style Guide 2 | 3 | ## Motivation - why use a formatting tool? 4 | 5 | Formatting code is a mostly mechanical task which takes both time and mental 6 | effort. By using an automatic formatting tool, a programmer is relieved of 7 | this task and can concentrate on more important things. 8 | 9 | Furthermore, by sticking to an established style guide (such as this one), 10 | programmers don't need to formulate ad hoc style rules, nor do they need to 11 | debate with other programmers what style rules should be used, saving time, 12 | communication overhead, and mental energy. 13 | 14 | Humans comprehend information through pattern matching. By ensuring that all 15 | Rust code has similar formatting, less mental effort is required to comprehend a 16 | new project, lowering the barrier to entry for new developers. 17 | 18 | Thus, there are productivity benefits to using a formatting tool (such as 19 | rustfmt), and even larger benefits by using a community-consistent formatting, 20 | typically by using a formatting tool's default settings. 21 | 22 | 23 | ## Formatting conventions 24 | 25 | ### Indentation and line width 26 | 27 | * Use spaces, not tabs. 28 | * Each level of indentation must be four spaces (that is, all indentation 29 | outside of string literals and comments must be a multiple of four). 30 | * The maximum width for a line is 100 characters. 31 | * A tool should be configurable for all three of these variables. 32 | 33 | 34 | ### Blank lines 35 | 36 | Separate items and statements by either zero or one blank lines (i.e., one or 37 | two newlines). E.g, 38 | 39 | ```rust 40 | fn foo() { 41 | let x = ...; 42 | 43 | let y = ...; 44 | let z = ...; 45 | } 46 | 47 | fn bar() {} 48 | fn baz() {} 49 | ``` 50 | 51 | Formatting tools should make the bounds on blank lines configurable: there 52 | should be separate minimum and maximum numbers of newlines between both 53 | statements and (top-level) items (i.e., four options). As described above, the 54 | defaults for both statements and items should be minimum: 1, maximum: 2. 55 | 56 | 57 | ### [Module-level items](items.md) 58 | ### [Statements](statements.md) 59 | ### [Expressions](expressions.md) 60 | ### [Types](types.md) 61 | 62 | 63 | ### Comments 64 | 65 | The following guidelines for comments are recommendations only, a mechanical 66 | formatter might skip formatting of comments. 67 | 68 | Prefer line comments (`//`) to block comments (`/* ... */`). 69 | 70 | When using line comments there should be a single space after the opening sigil. 71 | 72 | When using single-line block comments there should be a single space after the 73 | opening sigil and before the closing sigil. Multi-line block comments should 74 | have a newline after the opening sigil and before the closing sigil. 75 | 76 | Prefer to put a comment on its own line. Where a comment follows code, there 77 | should be a single space before it. Where a block comment is inline, there 78 | should be surrounding whitespace as if it were an identifier or keyword. There 79 | should be no trailing whitespace after a comment or at the end of any line in a 80 | multi-line comment. Examples: 81 | 82 | ```rust 83 | // A comment on an item. 84 | struct Foo { ... } 85 | 86 | fn foo() {} // A comment after an item. 87 | 88 | pub fn foo(/* a comment before an argument */ x: T) {...} 89 | ``` 90 | 91 | Comments should usually be complete sentences. Start with a capital letter, end 92 | with a period (`.`). An inline block comment may be treated as a note without 93 | punctuation. 94 | 95 | Source lines which are entirely a comment should be limited to 80 characters 96 | in length (including comment sigils, but excluding indentation) or the maximum 97 | width of the line (including comment sigils and indentation), whichever is 98 | smaller: 99 | 100 | ```rust 101 | // This comment goes up to the ................................. 80 char margin. 102 | 103 | { 104 | // This comment is .............................................. 80 chars wide. 105 | } 106 | 107 | { 108 | { 109 | { 110 | { 111 | { 112 | { 113 | // This comment is limited by the ......................... 100 char margin. 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | #### Doc comments 123 | 124 | Prefer line comments (`///`) to block comments (`/** ... */`). 125 | 126 | Prefer outer doc comments (`///` or `/** ... */`), only use inner doc comments 127 | (`//!` and `/*! ... */`) to write module-level or crate-level documentation. 128 | 129 | Doc comments should come before attributes. 130 | 131 | ### Attributes 132 | 133 | Put each attribute on its own line, indented to the level of the item. 134 | In the case of inner attributes (`#!`), indent it to the level of the inside of 135 | the item. Prefer outer attributes, where possible. 136 | 137 | For attributes with argument lists, format like functions. 138 | 139 | ```rust 140 | #[repr(C)] 141 | #[foo(foo, bar)] 142 | struct CRepr { 143 | #![repr(C)] 144 | x: f32, 145 | y: f32, 146 | } 147 | ``` 148 | 149 | For attributes with an equal sign, there should be a single space before and 150 | after the `=`, e.g., `#[foo = 42]`. 151 | 152 | There must only be a single `derive` attribute. Note for tool authors: if 153 | combining multiple `derive` attributes into a single attribute, the ordering of 154 | the derived names should be preserved. E.g., `#[derive(bar)] #[derive(foo)] 155 | struct Baz;` should be formatted to `#[derive(bar, foo)] struct Baz;`. 156 | 157 | ### *small* items 158 | 159 | In many places in this guide we specify that a formatter may format an item 160 | differently if it is *small*, for example struct literals: 161 | 162 | ```rust 163 | // Normal formatting 164 | Foo { 165 | f1: an_expression, 166 | f2: another_expression(), 167 | } 168 | 169 | // *small* formatting 170 | Foo { f1, f2 } 171 | ``` 172 | 173 | We leave it to individual tools to decide on exactly what *small* means. In 174 | particular, tools are free to use different definitions in different 175 | circumstances. 176 | 177 | Some suitable heuristics are the size of the item (in characters) or the 178 | complexity of an item (for example, that all components must be simple names, 179 | not more complex sub-expressions). For more discussion on suitable heuristics, 180 | see [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/47). 181 | 182 | Tools should give the user an option to ignore such heuristics and always use 183 | the normal formatting. 184 | 185 | 186 | ## [Non-formatting conventions](advice.md) 187 | 188 | ## [Cargo.toml conventions](cargo.md) 189 | 190 | ## [Principles used for deciding these guidelines](principles.md) 191 | -------------------------------------------------------------------------------- /style-guide/advice.md: -------------------------------------------------------------------------------- 1 | # Other style advice 2 | 3 | ## Expressions 4 | 5 | Prefer to use Rust's expression oriented nature where possible; 6 | 7 | ```rust 8 | // use 9 | let x = if y { 1 } else { 0 }; 10 | // not 11 | let x; 12 | if y { 13 | x = 1; 14 | } else { 15 | x = 0; 16 | } 17 | ``` 18 | 19 | ## Names 20 | 21 | * Types shall be `UpperCamelCase`, 22 | * Enum variants shall be `UpperCamelCase`, 23 | * Struct fields shall be `snake_case`, 24 | * Function and method names shall be `snake_case`, 25 | * Local variables shall be `snake_case`, 26 | * Macro names shall be `snake_case`, 27 | * Constants (`const`s and immutable `static`s) shall be `SCREAMING_SNAKE_CASE`. 28 | * When a name is forbidden because it is a reserved word (e.g., `crate`), use a 29 | trailing underscore to make the name legal (e.g., `crate_`), or use raw 30 | identifiers if possible. 31 | 32 | ### Modules 33 | 34 | Avoid `#[path]` annotations where possible. 35 | -------------------------------------------------------------------------------- /style-guide/cargo.md: -------------------------------------------------------------------------------- 1 | # Cargo.toml conventions 2 | 3 | ## Formatting conventions 4 | 5 | Use the same line width and indentation as Rust code. 6 | 7 | Put a blank line between the last key-value pair in a section and the header of 8 | the next section. Do not place a blank line between section headers and the 9 | key-value pairs in that section, or between key-value pairs in a section. 10 | 11 | Sort key names alphabetically within each section, with the exception of the 12 | `[package]` section. Put the `[package]` section at the top of the file; put 13 | the `name` and `version` keys in that order at the top of that section, 14 | followed by the remaining keys other than `description` in alphabetical order, 15 | followed by the `description` at the end of that section. 16 | 17 | Don't use quotes around any standard key names; use bare keys. Only use quoted 18 | keys for non-standard keys whose names require them, and avoid introducing such 19 | key names when possible. See the [TOML 20 | specification](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md#table) 21 | for details. 22 | 23 | Put a single space both before and after the `=` between a key and value. Do 24 | not indent any key names; start all key names at the start of a line. 25 | 26 | Use multi-line strings (rather than newline escape sequences) for any string 27 | values that include multiple lines, such as the crate description. 28 | 29 | For array values, such as a list of authors, put the entire list on the same 30 | line as the key, if it fits. Otherwise, use block indentation: put a newline 31 | after the opening square bracket, indent each item by one indentation level, 32 | put a comma after each item (including the last), and put the closing square 33 | bracket at the start of a line by itself after the last item. 34 | 35 | ```rust 36 | authors = [ 37 | "A Uthor ", 38 | "Another Author ", 39 | ] 40 | ``` 41 | 42 | For table values, such as a crate dependency with a path, write the entire 43 | table using curly braces and commas on the same line as the key if it fits. If 44 | the entire table does not fit on the same line as the key, separate it out into 45 | a separate section with key-value pairs: 46 | 47 | ```toml 48 | [dependencies] 49 | crate1 = { path = "crate1", version = "1.2.3" } 50 | 51 | [dependencies.extremely_long_crate_name_goes_here] 52 | path = "extremely_long_path_name_goes_right_here" 53 | version = "4.5.6" 54 | ``` 55 | 56 | ## Metadata conventions 57 | 58 | The authors list should consist of strings that each contain an author name 59 | followed by an email address in angle brackets: `Full Name `. 60 | It should not contain bare email addresses, or names without email addresses. 61 | (The authors list may also include a mailing list address without an associated 62 | name.) 63 | 64 | The license field must contain a valid [SPDX 65 | expression](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60), 66 | using valid [SPDX license names](https://spdx.org/licenses/). (As an exception, 67 | by widespread convention, the license field may use `/` in place of ` OR `; for 68 | example, `MIT/Apache-2.0`.) 69 | 70 | The homepage field, if present, must consist of a single URL, including the 71 | scheme (e.g. `https://example.org/`, not just `example.org`.) 72 | 73 | Within the description field, wrap text at 80 columns. Don't start the 74 | description field with the name of the crate (e.g. "cratename is a ..."); just 75 | describe the crate itself. If providing a multi-sentence description, the first 76 | sentence should go on a line by itself and summarize the crate, like the 77 | subject of an email or commit message; subsequent sentences can then describe 78 | the crate in more detail. 79 | -------------------------------------------------------------------------------- /style-guide/items.md: -------------------------------------------------------------------------------- 1 | ## Items 2 | 3 | `extern crate` statements must be first in a file. They must be ordered 4 | alphabetically. 5 | 6 | `use` statements, and module *declarations* (`mod foo;`, not `mod { ... }`) 7 | must come before other items. We recommend that imports come before module 8 | declarations; if imports and modules are separated, then they should be ordered 9 | alphabetically. When sorting, `self` and `super` must come before any other 10 | names. Module declarations should not be moved if they are annotated with 11 | `#[macro_use]`, since that may be semantics changing. 12 | 13 | Tools should make the above ordering optional. 14 | 15 | 16 | ### Function definitions 17 | 18 | In Rust, one finds functions by searching for `fn [function-name]`; It's 19 | important that you style your code so that it's very searchable in this way. 20 | 21 | The proper ordering and spacing is: 22 | 23 | ```rust 24 | [pub] [unsafe] [extern ["ABI"]] fn foo(arg1: i32, arg2: i32) -> i32 { 25 | ... 26 | } 27 | ``` 28 | 29 | Avoid comments within the signature itself. 30 | 31 | If the function signature does not fit on one line, then break after the opening 32 | parenthesis and before the closing parenthesis and put each argument on its own 33 | block-indented line. For example, 34 | 35 | ```rust 36 | fn foo( 37 | arg1: i32, 38 | arg2: i32, 39 | ) -> i32 { 40 | ... 41 | } 42 | ``` 43 | 44 | Note the trailing comma on the last argument. 45 | 46 | 47 | ### Tuples and tuple structs 48 | 49 | Write the type list as you would a parameter list to a function. 50 | 51 | Build a tuple or tuple struct as you would call a function. 52 | 53 | #### Single-line 54 | 55 | ```rust 56 | struct Bar(Type1, Type2); 57 | 58 | let x = Bar(11, 22); 59 | let y = (11, 22, 33); 60 | ``` 61 | 62 | ### Enums 63 | 64 | In the declaration, put each variant on its own line, block indented. 65 | 66 | Format each variant accordingly as either a struct, tuple struct, or identifier, 67 | which doesn't require special formatting (but without the `struct` keyword. 68 | 69 | ```rust 70 | enum FooBar { 71 | First(u32), 72 | Second, 73 | Error { 74 | err: Box, 75 | line: u32, 76 | }, 77 | } 78 | ``` 79 | 80 | If a struct variant is [*small*](#small-items), it may be formatted on 81 | one line. In this case, do not use a trailing comma for the field list, but do 82 | put spaces around each brace: 83 | 84 | ```rust 85 | enum FooBar { 86 | Error { err: Box, line: u32 }, 87 | } 88 | ``` 89 | 90 | In an enum with multiple struct variants, if any struct variant is written on 91 | multiple lines, then the multi-line formatting should be used for all struct 92 | variants. However, such a situation might be an indication that you should 93 | factor out the fields of the variant into their own struct. 94 | 95 | 96 | ### Structs and Unions 97 | 98 | Struct names follow on the same line as the `struct` keyword, with the opening 99 | brace on the same line when it fits within the right margin. All struct fields 100 | are indented once and end with a trailing comma. The closing brace is not 101 | indented and appears on its own line. 102 | 103 | ```rust 104 | struct Foo { 105 | a: A, 106 | b: B, 107 | } 108 | ``` 109 | 110 | If and only if the type of a field does not fit within the right margin, it is 111 | pulled down to its own line and indented again. 112 | 113 | ```rust 114 | struct Foo { 115 | a: A, 116 | long_name: 117 | LongType, 118 | } 119 | ``` 120 | 121 | Prefer using a unit struct (e.g., `struct Foo;`) to an empty struct (e.g., 122 | `struct Foo();` or `struct Foo {}`, these only exist to simplify code 123 | generation), but if you must use an empty struct, keep it on one line with no 124 | space between the braces: `struct Foo;` or `struct Foo {}`. 125 | 126 | The same guidelines are used for untagged union declarations. 127 | 128 | ```rust 129 | union Foo { 130 | a: A, 131 | b: B, 132 | long_name: 133 | LongType, 134 | } 135 | ``` 136 | 137 | 138 | ### Tuple structs 139 | 140 | Put the whole struct on one line if possible. Types in the parentheses should be 141 | separated by a comma and space with no trailing comma. No spaces around the 142 | parentheses or semi-colon: 143 | 144 | ```rust 145 | pub struct Foo(String, u8); 146 | ``` 147 | 148 | Prefer unit structs to empty tuple structs (these only exist to simplify code 149 | generation), e.g., `struct Foo;` rather than `struct Foo();`. 150 | 151 | For more than a few fields, prefer a proper struct with named fields. Given 152 | this, a tuple struct should always fit on one line. If it does not, block format 153 | the fields with a field on each line and a trailing comma: 154 | 155 | ```rust 156 | pub struct Foo( 157 | String, 158 | u8, 159 | ); 160 | ``` 161 | 162 | 163 | ### Traits 164 | 165 | Trait items should be block-indented. If there are no items, the trait may be 166 | formatted on a single line. Otherwise there should be line-breaks after the 167 | opening brace and before the closing brace: 168 | 169 | ```rust 170 | trait Foo {} 171 | 172 | pub trait Bar { 173 | ... 174 | } 175 | ``` 176 | 177 | If the trait has bounds, there should be a space after the colon but not before 178 | and before and after each `+`, e.g., 179 | 180 | ```rust 181 | trait Foo: Debug + Bar {} 182 | ``` 183 | 184 | Prefer not to line-break in the bounds if possible (consider using a `where` 185 | clause). Prefer to break between bounds than to break any individual bound. If 186 | you must break the bounds, put each bound (including the first) on its own 187 | block-indented line, break before the `+` and put the opening brace on its own 188 | line: 189 | 190 | ```rust 191 | pub trait IndexRanges: 192 | Index, Output=Self> 193 | + Index, Output=Self> 194 | + Index, Output=Self> 195 | + Index 196 | { 197 | ... 198 | } 199 | ``` 200 | 201 | 202 | ### Impls 203 | 204 | Impl items should be block indented. If there are no items, the impl may be 205 | formatted on a single line. Otherwise there should be line-breaks after the 206 | opening brace and before the closing brace: 207 | 208 | ```rust 209 | impl Foo {} 210 | 211 | impl Bar for Foo { 212 | ... 213 | } 214 | ``` 215 | 216 | Avoid line-breaking in the signature if possible. If a line break is required in 217 | a non-inherent impl, break immediately before `for`, block indent the concrete type 218 | and put the opening brace on its own line: 219 | 220 | ```rust 221 | impl Bar 222 | for Foo 223 | { 224 | ... 225 | } 226 | ``` 227 | 228 | 229 | ### Extern crate 230 | 231 | `extern crate foo;` 232 | 233 | Use spaces around keywords, no spaces around the semi-colon. 234 | 235 | 236 | ### Modules 237 | 238 | ```rust 239 | mod foo { 240 | } 241 | ``` 242 | 243 | ```rust 244 | mod foo; 245 | ``` 246 | 247 | Use spaces around keywords and before the opening brace, no spaces around the 248 | semi-colon. 249 | 250 | ### macro\_rules! 251 | 252 | Use `{}` for the full definition of the macro. 253 | 254 | ```rust 255 | macro_rules! foo { 256 | } 257 | ``` 258 | 259 | 260 | ### Generics 261 | 262 | Prefer to put a generics clause on one line. Break other parts of an item 263 | declaration rather than line-breaking a generics clause. If a generics clause is 264 | large enough to require line-breaking, you should prefer to use a `where` clause 265 | instead. 266 | 267 | Do not put spaces before or after `<` nor before `>`. Only put a space after `>` 268 | if it is followed by a word or opening brace, not an opening parenthesis. There 269 | should be a space after each comma and no trailing comma. 270 | 271 | ```rust 272 | fn foo(x: Vec, y: Vec) ... 273 | 274 | impl SomeType { ... 275 | ``` 276 | 277 | If the generics clause must be formatted across multiple lines, each parameter 278 | should have its own block-indented line, there should be newlines after the 279 | opening bracket and before the closing bracket, and the should be a trailing 280 | comma. 281 | 282 | ```rust 283 | fn foo< 284 | T: Display, 285 | U: Debug, 286 | >(x: Vec, y: Vec) ... 287 | ``` 288 | 289 | If an associated type is bound in a generic type, then there should be spaces on 290 | either side of the `=`: 291 | 292 | ```rust 293 | > 294 | ``` 295 | 296 | Prefer to use single-letter names for generic parameters. 297 | 298 | 299 | ### `where` clauses 300 | 301 | These rules apply for `where` clauses on any item. 302 | 303 | A `where` clause may immediately follow a closing bracket of any kind. 304 | Otherwise, it must start a new line, with no indent. Each component of a `where` 305 | clause must be on its own line and be block indented. There should be a trailing 306 | comma, unless the clause is terminated with a semicolon. If the `where` clause 307 | is followed by a block (or assignment), the block should be started on a new 308 | line. Examples: 309 | 310 | ```rust 311 | fn function(args) 312 | where 313 | T: Bound, 314 | U: AnotherBound, 315 | { 316 | body 317 | } 318 | 319 | fn foo( 320 | args 321 | ) -> ReturnType 322 | where 323 | T: Bound, 324 | { 325 | body 326 | } 327 | 328 | fn foo( 329 | args, 330 | ) where 331 | T: Bound, 332 | U: AnotherBound, 333 | { 334 | body 335 | } 336 | 337 | fn foo( 338 | args 339 | ) -> ReturnType 340 | where 341 | T: Bound, 342 | U: AnotherBound; // Note, no trailing comma. 343 | 344 | // Note that where clauses on `type` aliases are not enforced and should not 345 | // be used. 346 | type Foo 347 | where 348 | T: Bound 349 | = Bar; 350 | ``` 351 | 352 | If a `where` clause is very short, we recommend using an inline bound on the 353 | type parameter. 354 | 355 | 356 | If a component of a `where` clause is long, it may be broken before `+` and 357 | further block indented. Each bound should go on its own line. E.g., 358 | 359 | ```rust 360 | impl IndexRanges for T 361 | where 362 | T: Index, Output = Self::Output> 363 | + Index, Output = Self::Output> 364 | + Index, Output = Self::Output> 365 | + Index, Output = Self::Output> 366 | + Index, Output = Self::Output> + Index 367 | ``` 368 | 369 | #### Option - `where_single_line` 370 | 371 | `where_single_line` is `false` by default. If `true`, then a where clause with 372 | exactly one component may be formatted on a single line if the rest of the 373 | item's signature is also kept on one line. In this case, there is no need for a 374 | trailing comma and if followed by a block, no need for a newline before the 375 | block. E.g., 376 | 377 | ```rust 378 | // May be single-lined. 379 | fn foo(args) -> ReturnType 380 | where T: Bound { 381 | body 382 | } 383 | 384 | // Must be multi-lined. 385 | fn foo( 386 | args 387 | ) -> ReturnType 388 | where 389 | T: Bound, 390 | { 391 | body 392 | } 393 | ``` 394 | 395 | 396 | ### Type aliases 397 | 398 | Type aliases should generally be kept on one line. If necessary to break the 399 | line, do so after the `=`; the right-hand-side should be block indented: 400 | 401 | ```rust 402 | pub type Foo = Bar; 403 | 404 | // If multi-line is required 405 | type VeryLongType = 406 | AnEvenLongerType>; 407 | ``` 408 | 409 | Where possible avoid `where` clauses and keep type constraints inline. Where 410 | that is not possible split the line before and after the `where` clause (and 411 | split the `where` clause as normal), e.g., 412 | 413 | ```rust 414 | type VeryLongType 415 | where 416 | T: U::AnAssociatedType, 417 | U: SomeBound, 418 | = AnEvenLongerType>; 419 | ``` 420 | 421 | 422 | ### Associated types 423 | 424 | Associated types should follow the guidelines above for type aliases. Where an 425 | associated type has a bound, there should be a space after the colon but not 426 | before: 427 | 428 | ```rust 429 | pub type Foo: Bar; 430 | ``` 431 | 432 | 433 | ### extern items 434 | 435 | When writing extern items (such as `extern "C" fn`), always be explicit about 436 | the ABI. For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or 437 | `extern "C" { ... }`. 438 | 439 | 440 | ### Imports (`use` statements) 441 | 442 | If an import can be formatted on one line, do so. There should be no spaces 443 | around braces. 444 | 445 | ```rust 446 | use a::b::c; 447 | use a::b::d::*; 448 | use a::b::{foo, bar, baz}; 449 | ``` 450 | 451 | 452 | #### Large list imports 453 | 454 | Prefer to use multiple imports rather than a multi-line import. However, tools 455 | should not split imports by default (they may offer this as an option). 456 | 457 | If an import does require multiple lines (either because a list of single names 458 | does not fit within the max width, or because of the rules for nested imports 459 | below), then break after the opening brace and before the closing brace, use a 460 | trailing comma, and block indent the names. 461 | 462 | 463 | ```rust 464 | // Prefer 465 | foo::{long, list, of, imports}; 466 | foo::{more, imports}; 467 | 468 | // If necessary 469 | foo::{ 470 | long, list, of, imports, more, 471 | imports, // Note trailing comma 472 | }; 473 | ``` 474 | 475 | 476 | #### Ordering of imports 477 | 478 | A *group* of imports is a set of imports on the same or sequential lines. One or 479 | more blank lines or other items (e.g., a function) separate groups of imports. 480 | 481 | Within a group of imports, imports must be sorted ascii-betically. Groups of 482 | imports must not be merged or re-ordered. 483 | 484 | 485 | E.g., input: 486 | 487 | ```rust 488 | use d; 489 | use c; 490 | 491 | use b; 492 | use a; 493 | ``` 494 | 495 | output: 496 | 497 | ```rust 498 | use c; 499 | use d; 500 | 501 | use a; 502 | use b; 503 | ``` 504 | 505 | Because of `macro_use`, attributes must also start a new group and prevent 506 | re-ordering. 507 | 508 | Note that tools which only have access to syntax (such as Rustfmt) cannot tell 509 | which imports are from an external crate or the std lib, etc. 510 | 511 | 512 | #### Ordering list import 513 | 514 | Names in a list import must be sorted ascii-betically, but with `self` and 515 | `super` first, and groups and glob imports last. This applies recursively. For 516 | example, `a::*` comes before `b::a` but `a::b` comes before `a::*`. E.g., 517 | `use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, b::{self, r, s}};`. 518 | 519 | 520 | #### Normalisation 521 | 522 | Tools must make the following normalisations: 523 | 524 | * `use a::self;` -> `use a;` 525 | * `use a::{};` -> (nothing) 526 | * `use a::{b};` -> `use a::b;` 527 | 528 | And must apply these recursively. 529 | 530 | Tools must not otherwise merge or un-merge import lists or adjust glob imports 531 | (without an explicit option). 532 | 533 | 534 | #### Nested imports 535 | 536 | If there are any nested imports in a list import, then use the multi-line form, 537 | even if the import fits on one line. Each nested import must be on its own line, 538 | but non-nested imports must be grouped on as few lines as possible. 539 | 540 | For example, 541 | 542 | ```rust 543 | use a::b::{ 544 | x, y, z, 545 | u::{...}, 546 | w::{...}, 547 | }; 548 | ``` 549 | 550 | 551 | #### Merging/un-merging imports 552 | 553 | An example: 554 | 555 | ```rust 556 | // Un-merged 557 | use a::b; 558 | use a::c::d; 559 | 560 | // Merged 561 | use a::{b, c::d}; 562 | ``` 563 | 564 | Tools must not merge or un-merge imports by default. They may offer merging or 565 | un-merging as an option. 566 | -------------------------------------------------------------------------------- /style-guide/principles.md: -------------------------------------------------------------------------------- 1 | # Guiding principles and rationale 2 | 3 | When deciding on style guidelines, the style team tried to be guided by the 4 | following principles (in rough priority order): 5 | 6 | * readability 7 | - scan-ability 8 | - avoiding misleading formatting 9 | - accessibility - readable and editable by users using the the widest 10 | variety of hardware, including non-visual accessibility interfaces 11 | - readability of code when quoted in rustc error messages 12 | 13 | * aesthetics 14 | - sense of 'beauty' 15 | - consistent with other languages/tools 16 | 17 | * specifics 18 | - compatibility with version control practices - preserving diffs, 19 | merge-friendliness, etc. 20 | - preventing right-ward drift 21 | - minimising vertical space 22 | 23 | * application 24 | - ease of manual application 25 | - ease of implementation (in Rustfmt, and in other tools/editors/code generators) 26 | - internal consistency 27 | - simplicity of formatting rules 28 | 29 | 30 | ## Overarching guidelines 31 | 32 | Prefer block indent over visual indent. E.g., 33 | 34 | ```rust 35 | // Block indent 36 | a_function_call( 37 | foo, 38 | bar, 39 | ); 40 | 41 | // Visual indent 42 | a_function_call(foo, 43 | bar); 44 | ``` 45 | 46 | This makes for smaller diffs (e.g., if `a_function_call` is renamed in the above 47 | example) and less rightward drift. 48 | 49 | Lists should have a trailing comma when followed by a newline, see the block 50 | indent example above. This choice makes moving code (e.g., by copy and paste) 51 | easier and makes smaller diffs. 52 | -------------------------------------------------------------------------------- /style-guide/statements.md: -------------------------------------------------------------------------------- 1 | ### Let statements 2 | 3 | There should be spaces after the `:` and on both sides of the `=` (if they are 4 | present). No space before the semi-colon. 5 | 6 | ```rust 7 | // A comment. 8 | let pattern: Type = expr; 9 | 10 | let pattern; 11 | let pattern: Type; 12 | let pattern = expr; 13 | ``` 14 | 15 | If possible the declaration should be formatted on a single line. If this is not 16 | possible, then try splitting after the `=`, if the declaration can fit on two 17 | lines. The expression should be block indented. 18 | 19 | ```rust 20 | let pattern: Type = 21 | expr; 22 | ``` 23 | 24 | If the first line does not fit on a single line, then split after the colon, 25 | using block indentation. If the type covers multiple lines, even after line- 26 | breaking after the `:`, then the first line may be placed on the same line as 27 | the `:`, subject to the [combining rules](https://github.com/rust-lang-nursery/fmt-rfcs/issues/61) (WIP). 28 | 29 | 30 | ```rust 31 | let pattern: 32 | Type = 33 | expr; 34 | ``` 35 | 36 | e.g, 37 | 38 | ```rust 39 | let Foo { 40 | f: abcd, 41 | g: qwer, 42 | }: Foo = 43 | Foo { f, g }; 44 | 45 | let (abcd, 46 | defg): 47 | Baz = 48 | { ... } 49 | ``` 50 | 51 | If the expression covers multiple lines, if the first line of the expression 52 | fits in the remaining space, it stays on the same line as the `=`, the rest of the 53 | expression is not indented. If the first line does not fit, then it should start 54 | on the next lines, and should be block indented. If the expression is a block 55 | and the type or pattern cover multiple lines, then the opening brace should be 56 | on a new line and not indented (this provides separation for the interior of the 57 | block from the type), otherwise the opening brace follows the `=`. 58 | 59 | Examples: 60 | 61 | ```rust 62 | let foo = Foo { 63 | f: abcd, 64 | g: qwer, 65 | }; 66 | 67 | let foo = 68 | ALongName { 69 | f: abcd, 70 | g: qwer, 71 | }; 72 | 73 | let foo: Type = { 74 | an_expression(); 75 | ... 76 | }; 77 | 78 | let foo: 79 | ALongType = 80 | { 81 | an_expression(); 82 | ... 83 | }; 84 | 85 | let Foo { 86 | f: abcd, 87 | g: qwer, 88 | }: Foo = Foo { 89 | f: blimblimblim, 90 | g: blamblamblam, 91 | }; 92 | 93 | let Foo { 94 | f: abcd, 95 | g: qwer, 96 | }: Foo = foo( 97 | blimblimblim, 98 | blamblamblam, 99 | ); 100 | ``` 101 | 102 | 103 | ### Macros in statement position 104 | 105 | A macro use in statement position should use parentheses or square brackets as 106 | delimiters and should be terminated with a semi-colon. There should be no spaces 107 | between the name, `!`, the delimiters, or the `;`. 108 | 109 | ```rust 110 | // A comment. 111 | a_macro!(...); 112 | ``` 113 | 114 | 115 | ### Expressions in statement position 116 | 117 | There should be no space between the expression and the semi-colon. 118 | 119 | ``` 120 | ; 121 | ``` 122 | 123 | All expressions in statement position should be terminated with a semi-colon, 124 | unless they end with a block or are used as the value for a block. 125 | 126 | E.g., 127 | 128 | ```rust 129 | { 130 | an_expression(); 131 | expr_as_value() 132 | } 133 | 134 | return foo(); 135 | 136 | loop { 137 | break; 138 | } 139 | ``` 140 | 141 | Use a semi-colon where an expression has void type, even if it could be 142 | propagated. E.g., 143 | 144 | ```rust 145 | fn foo() { ... } 146 | 147 | fn bar() { 148 | foo(); 149 | } 150 | ``` 151 | -------------------------------------------------------------------------------- /style-guide/types.md: -------------------------------------------------------------------------------- 1 | ## Types and Bounds 2 | 3 | ### Single line formatting 4 | 5 | * `[T]` no spaces 6 | * `[T; expr]`, e.g., `[u32; 42]`, `[Vec; 10 * 2 + foo()]` (space after colon, no spaces around square brackets) 7 | * `*const T`, `*mut T` (no space after `*`, space before type) 8 | * `&'a T`, `&T`, `&'a mut T`, `&mut T` (no space after `&`, single spaces separating other words) 9 | * `unsafe extern "C" fn<'a, 'b, 'c>(T, U, V) -> W` or `fn()` (single spaces around keyowrds and sigils, and after commas, no trailing commas, no spaces around brackets) 10 | * `!` should be treated like any other type name, `Name` 11 | * `(A, B, C, D)` (spaces after commas, no spaces around parens, no trailing comma unless it is a one-tuple) 12 | * ` as SomeTrait>::Foo::Bar` or `Foo::Bar` or `::Foo::Bar` (no spaces around `::` or angle brackets, single spaces around `as`) 13 | * `Foo::Bar` (spaces after commas, no trailing comma, no spaces around angle brackets) 14 | * `T + T + T` (single spaces between types, and `+`). 15 | * `impl T + T + T` (single spaces between keyword, types, and `+`). 16 | 17 | Parentheses used in types should not be surrounded by whitespace, e.g., `(Foo)` 18 | 19 | 20 | ### Line breaks 21 | 22 | Avoid breaking lines in types where possible. Prefer breaking at outermost scope, e.g., prefer 23 | 24 | ```rust 25 | Foo< 26 | Bar, 27 | Baz, 28 | > 29 | ``` 30 | 31 | to 32 | 33 | ```rust 34 | Foo> 38 | ``` 39 | 40 | `[T; expr]` may be broken after the `;` if necessary. 41 | 42 | Function types may be broken following the rules for function declarations. 43 | 44 | Generic types may be broken following the rules for generics. 45 | 46 | Types with `+` may be broken after any `+` using block indent and breaking before the `+`. When breaking such a type, all `+`s should be line broken, e.g., 47 | 48 | ```rust 49 | impl Clone 50 | + Copy 51 | + Debug 52 | 53 | Box< 54 | Clone 55 | + Copy 56 | + Debug 57 | > 58 | ``` 59 | -------------------------------------------------------------------------------- /text/0049-match-arm-attributes.md: -------------------------------------------------------------------------------- 1 | - Start Date: 2014-03-20 2 | - RFC PR: [rust-lang/rfcs#49](https://github.com/rust-lang/rfcs/pull/49) 3 | - Rust Issue: [rust-lang/rust#12812](https://github.com/rust-lang/rust/issues/12812) 4 | - Translators: [[@FizzyElt](https://github.com/FizzyElt)] 5 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/0e2baed56a1d31a65f58f4cb615eedfaad59c2e3/text/0049-match-arm-attributes.md) 6 | - Updated: 2022-05-17 7 | 8 | # 概要 9 | 10 | 允許屬性在配對分支上 11 | 12 | # 動機 13 | 14 | 有時希望使用屬性來注釋配對語句的分支,例如條件編譯 `#[cfg]` 或是分支權重(後者為最重要的用途)。 15 | 16 | 17 | 對於條件編譯,暫時得解決辦法是使用 `#[cfg]` 來重複宣告相同函式來處理不同的案例。一個案例研究是 [sfackler 的 bindings to OpenSSL](https://github.com/sfackler/rust-openssl),在多數的發行版本中移除了 SSLv2 支持,因此 Rust bindings 部分需要被條件禁用。支援各種不同 SSL 版本最顯而易見的方法是使用枚舉。 18 | 19 | ```rust 20 | pub enum SslMethod { 21 | #[cfg(sslv2)] 22 | /// Only support the SSLv2 protocol 23 | Sslv2, 24 | /// Only support the SSLv3 protocol 25 | Sslv3, 26 | /// Only support the TLSv1 protocol 27 | Tlsv1, 28 | /// Support the SSLv2, SSLv3 and TLSv1 protocols 29 | Sslv23, 30 | } 31 | ``` 32 | 33 | 然而,所有的 `match` 只能在 `cfg` 啟用時使用 `Sslv2`,例如下面內容是無效的: 34 | 35 | ```rust 36 | fn name(method: SslMethod) -> &'static str { 37 | match method { 38 | Sslv2 => "SSLv2", 39 | Sslv3 => "SSLv3", 40 | _ => "..." 41 | } 42 | } 43 | ``` 44 | 45 | 一個有效的方法則必須有兩個定義:`#[cfg(sslv2)] fn 46 | name(...)` 和 `#[cfg(not(sslv2)] fn name(...)` 47 | 。前者有 `Sslv2` 的分支,後者沒有。顯然,對於枚舉中每個額外的 `cfg` 變體,這都會以指數型的方式爆炸。 48 | 49 | 分支權重將允許仔細的微優化器(micro-optimiser)通知編譯器,例如,鮮少採取某個配對分支: 50 | 51 | ```rust 52 | match foo { 53 | Common => {} 54 | #[cold] 55 | Rare => {} 56 | } 57 | ``` 58 | 59 | # 詳細設計 60 | 61 | 一般的屬性語法,應用於整個配對分支。 62 | 63 | ```rust 64 | match x { 65 | #[attr] 66 | Thing => {} 67 | 68 | #[attr] 69 | Foo | Bar => {} 70 | 71 | #[attr] 72 | _ => {} 73 | } 74 | ``` 75 | 76 | # 替代方案 77 | 78 | 實際上沒有通用的替代方案; 人們也許可以用一些巨集(macro)和輔助函數來解決條件枚舉變體的配對問題; 但一般來說,這起不了任何作用。 79 | 80 | # 未解決的問題 81 | 82 | 無 -------------------------------------------------------------------------------- /text/0198-slice-notation.md: -------------------------------------------------------------------------------- 1 | - Start Date: 2014-09-11 2 | - RFC PR #: [rust-lang/rfcs#198](https://github.com/rust-lang/rfcs/pull/198) 3 | - Rust Issue #: [rust-lang/rust#17177](https://github.com/rust-lang/rust/issues/17177) 4 | - Translators: [[@FizzyElt](https://github.com/FizzyElt)] 5 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/4009b546172c558a1cfa0f39dd81c896312f73d5/text/0198-slice-notation.md) 6 | - Updated: 2022-05-27 7 | 8 | # 概要 9 | 10 | 此 RFC 增加了*多載切片符號*: 11 | 12 | - `foo[]` 對應 `foo.as_slice()` 13 | - `foo[n..m]` 對應 `foo.slice(n, m)` 14 | - `foo[n..]` 對應 `foo.slice_from(n)` 15 | - `foo[..m]` 對應 `foo.slice_to(m)` 16 | - 以上所有的 `mut` 變體 17 | 18 | 透過`Slice` 跟 `SliceMut` 兩個新特徵(trait)。 19 | 20 | 它還將 範圍`配對`模式的符號改為 `...`,以表示它們是包含的,而 `..` 在切片中是不包含的。 21 | 22 | # 動機 23 | 24 | 引入此功能有兩個主要動機。 25 | 26 | ### 人因工程 27 | 28 | 在處理 vector 或其他類型的容器時,切片操作(特別是 `as_slice`)是相當常見的基本操作。我們已經有了透過 `Index` 特徵進行索引的語法,此 RFC 本質上是這特徵的延續。 29 | 30 | `as_slice` 運算子尤其重要,自從我們已經擺脫以強迫的方式自動切片,明確地呼叫 `as_slice` 變得非常普遍,這也是語言中[人因工程優先/第一印象](https://github.com/rust-lang/rust/issues/14983)的問題之一。還有一些其他方法可以解決這個特定問題,但這些替代方法具有下方討論的一些缺點(參閱「替代方案」)。 31 | 32 | ### 錯誤處理協定 33 | 34 | 我們正在逐漸走向一個類似 Python 的世界,像是當 `n` 超出範圍時,`foo[n]` 會呼叫 `fail!`,而相應的方法像是 `get` 則返回 `Option` 而不是失敗。透過為切片提供類似的符號,我們打開了在整個 vector-like APIs 中遵循相同協定的大門。 35 | 36 | # 詳細設計 37 | 38 | 此設計是對 `Index` 特徵設計的直接延續。我們引入了兩個新特徵(trait),用於不可變與可變切片: 39 | 40 | ```rust 41 | trait Slice { 42 | fn as_slice<'a>(&'a self) -> &'a S; 43 | fn slice_from(&'a self, from: Idx) -> &'a S; 44 | fn slice_to(&'a self, to: Idx) -> &'a S; 45 | fn slice(&'a self, from: Idx, to: Idx) -> &'a S; 46 | } 47 | 48 | trait SliceMut { 49 | fn as_mut_slice<'a>(&'a mut self) -> &'a mut S; 50 | fn slice_from_mut(&'a mut self, from: Idx) -> &'a mut S; 51 | fn slice_to_mut(&'a mut self, to: Idx) -> &'a mut S; 52 | fn slice_mut(&'a mut self, from: Idx, to: Idx) -> &'a mut S; 53 | } 54 | ``` 55 | 56 | (請注意,此處的可變名稱是命名規則可能更改的一部分,將在單獨的 RFC 中描述)。 57 | 58 | 這些特徵將在解釋以下符號時使用: 59 | 60 | *不可變切片* 61 | 62 | - `foo[]` 對應 `foo.as_slice()` 63 | - `foo[n..m]` 對應 `foo.slice(n, m)` 64 | - `foo[n..]` 對應 `foo.slice_from(n)` 65 | - `foo[..m]` 對應 `foo.slice_to(m)` 66 | 67 | *可變切片* 68 | 69 | - `foo[mut]` 對應 `foo.as_mut_slice()` 70 | - `foo[mut n..m]` 對應 `foo.slice_mut(n, m)` 71 | - `foo[mut n..]` 對應 `foo.slice_from_mut(n)` 72 | - `foo[mut ..m]` 對應 `foo.slice_to_mut(m)` 73 | 74 | 像 `Index` 一樣,這種表示法的使用將自動取值,就如同對應的方法調用一樣。因此,如果 `T` 實現了 `Slice` 和 `s: Smaht`,那麼 `s[]` 就可以編譯並具有 `&[U]` 類型。 75 | 76 | 請注意,切片是「不包含」(因此 `[n..m]` 是等於 `n <= x < m`),而模式配對中的 `..` 是「包含」。為了避免混淆,我們建議將配對符號更改為 `...` 來做出區別。更改符號而不是解釋的原因是,「不包含」(分別「包含」)解釋是切片(相較於「配對」)的正確默認值。 77 | 78 | ## 使用此語法的基本理由 79 | 80 | 用於切片的方括號的選擇很簡單:它與我們的索引表示法契合,並且切片和索引密切相關。 81 | 82 | 其他一些語言(如 Python 和 Go 和 Fortran)在切片表示法中使用 `:` 而不是 `..`。在 Rust 中,`..` 的選擇受到 Rust 本身其他地方的使用影響,例如固定長度陣列類型 `[T, ..n]`。用於切片的 `..` 在 Perl 和 D 中有先例。 83 | 84 | 有關程式語言中切片表示法歷史的更多資訊,請參閱 [維基百科](http://en.wikipedia.org/wiki/Array_slicing)。 85 | 86 | ### `mut` 限定符 87 | 88 | 可能令人驚訝的是,`mut` 在建議的切片表示法中作為限定符,而不是用於索引表示法。原因是索引包含隱式取消引用。假如 `v: Vec` 則 `v[n]` 的類型為 `Foo`,而不是 `&Foo` 或 `&mut Foo`。因此,如果你想透過索引取得可變引用,你可以寫成 `&mut v[n]`。更廣泛地說,這允許我們在解決可變性之前進行解析/類型檢查。 89 | 90 | 這種對 `Index` 的處理符合 C 的傳統,並允許我們寫成 `v[0] = foo`,而不是 `*v[0] = foo`。 91 | 92 | 另一方面,這種方法對於切片是有問題的,因為一般來說,他會產生一個 unsized 類型(在 DST 中),當然,切片是為了給你一個指向切片大小的胖指標,我們不希望立即取消引用。但是,這樣的結果是,我們需要預先知道切片的可變性,因為它決定了表達式的類型。 93 | 94 | # 缺點 95 | 96 | 主要的缺點是增加了語言語法的複雜度。這看起來微不足道,尤其是因為這裡的符號本質上是 "完成" 以 `Index` 特徵開始的內容。 97 | 98 | ## 設計上的限制 99 | 100 | 與 `Index` 特徵一樣,這迫使結果透過 `&` 成為一個引用,這可能會排除切片的普遍化。 101 | 102 | 解決此問題的一個方法是切片方法使用 `self` (依值) 而不是 `&self`,反過來在 `&T` 而不是 `T` 上實現特徵。這個方法是否長期可行將取決於方法解析和自動引用的最終規則。 103 | 104 | 一般來說,當特徵可以應用於類型 `T` 而不是借用類型 `&T` 時,特徵系統運作的最好。最終,如果 Rust 獲得了更高的類型(HKT),我們可以將特徵中的切片類型 `S` 更改為更高的類型,這樣他就是一個由生命週期索引的類型*家族*。然後,我們可以用 `S<'a>` 來代替返回值中的 `&'a S`。在未來,我們應該可以從當前的 `Index` 和 `Slice` 特徵設計轉換到一個 HKT 版本,而不會破壞相後的兼容性,方法是對實現舊特徵的類型使用新特徵的全面實現(例如 `IndexHKT`)。 105 | 106 | # 替代方案 107 | 108 | 對於改善 `as_slice` 的易用性,有兩個主要替代方案。 109 | 110 | ## 強制: 自動切片 111 | 112 | 一種可能性是重新引入某種自動切片的強制。我們曾經有一個從(用現今的話來說)`Vec` 到 `&[T]` 的強制轉換。由於我們不再強制擁有借來的值,我們現在可能想要一個強制 `&Vec` 到 `&[T]` 的轉換: 113 | 114 | ```rust 115 | fn use_slice(t: &[u8]) { ... } 116 | 117 | let v = vec!(0u8, 1, 2); 118 | use_slice(&v) // automatically coerce here 119 | use_slice(v.as_slice()) // equivalent 120 | ``` 121 | 122 | 不幸的是,添加這樣的強制需要在以下選項中進行選擇: 123 | 124 | * 將強制與 `Vec` 和 `String` 連繫起來,這將重新引入對這些原本純粹的函式庫類型的特殊處理,並且意味著其他支援切片的函數庫類型不會受益(違背了一些 DST 的目的)。 125 | 126 | * 透過特徵使強制可擴展。然而這是在打開潘朵拉的盒子:這個機制很可能被用來在強制的過程中運行任意的程式碼,所以任何呼叫 `foo(a, b, c)` 都可能涉及運行程式碼來預處理每個參數。雖然我們最終可能想要這種使用者可擴展的強制機制,但在理解程式碼時,這是一個很**大**的步驟,有很多潛在的不利因素,所以我們應該先追求更保守的解決方案。 127 | 128 | ## 解引用 129 | 130 | 另一種可能是讓 `String` 實現 `Deref` 和 `Vec` 實現 `Deref<[T]>` 一旦 DST 登陸。這樣做將允許顯式的強制機制,例如: 131 | 132 | ```rust 133 | fn use_slice(t: &[u8]) { ... } 134 | 135 | let v = vec!(0u8, 1, 2); 136 | use_slice(&*v) // take advantage of deref 137 | use_slice(v.as_slice()) // equivalent 138 | ``` 139 | 140 | 但是,這樣做至少有兩個缺點: 141 | 142 | * 目前不清楚方法解析規則最終將如何與 `Deref` 互動。特別是,一個領先的提議是,對於一個智慧指標 `s: Smaht` ,當你呼叫 `s.m(...)` 時,對於 `Smaht` 只有 *內置* 方法 `m` 被考慮到;僅考慮最大引用值 `*s` 的特徵方法。 143 | 144 | 使用這樣的解析策略,為 `Vec` 實現 `Deref` 將使得我們無法在 `Vec` 類型上使用特徵方法 ,除非透過 UFCS,這嚴重限制了程式設計師為 `Vec` 有效地實現新特徵的能力。 145 | 146 | * 將 `Vec` 作為一個圍繞切片的智慧指標的想法,以及如上所述的 `&*v` 的使用,有點違反直覺,特別是對於這樣的基本類型。 147 | 148 | 追根究底,無論如何,切片的符號本身似乎是可取的,如果它能消除對 `Vec` 和 `String` 實現 `Deref` 的需要,那就更好了。 -------------------------------------------------------------------------------- /text/1548-global-asm.md: -------------------------------------------------------------------------------- 1 | - Feature Name: global_asm 2 | - Start Date: 2016-03-18 3 | - RFC PR: [rust-lang/rfcs#1548](https://github.com/rust-lang/rfcs/pull/1548) 4 | - Rust Issue: [rust-lang/rust#35119](https://github.com/rust-lang/rust/issues/35119) 5 | - Translators: [[@TSOTSI1](https://github.com/TSOTSI1)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/f4b8b61a414298ba0f76d9b786d58ccdc34a44bb/text/1548-global-asm.md) 7 | - Updated: 2022-06-29 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 此 RFC 透過添加 `global_asm!` 巨集(macro)公開了 LLVM 對 [module-level inline assembly](http://llvm.org/docs/LangRef.html#module-level-inline-assembly) 的支援。語法非常簡單:它只需要一個包含組合語言代碼的字串字面值。 13 | 14 | 範例: 15 | ```rust 16 | global_asm!(r#" 17 | .globl my_asm_func 18 | my_asm_func: 19 | ret 20 | "#); 21 | 22 | extern { 23 | fn my_asm_func(); 24 | } 25 | ``` 26 | 27 | # 動機 28 | [動機]: #動機 29 | 30 | 此功能有兩個主要使用案例。首先,它允許完全在組合語言中寫入函式,這大大消除了對 `naked` 屬性的需要。這主要適用於使用自訂呼叫規則(如中斷處置器)的函式。 31 | 32 | 另一個重要的使用案例是,它允許外部組合語言檔案在 Rust 模組中使用,而無需駭入編譯系統。 33 | 34 | ```rust 35 | global_asm!(include_str!("my_asm_file.s")); 36 | ``` 37 | 38 | 組合語言檔案也可以由 `build.rs` 預處理或產生(例如使用 C 預處理器),這將在 Cargo 輸出目錄中產生輸出檔案: 39 | 40 | ```rust 41 | global_asm!(include_str!(concat!(env!("OUT_DIR"), "/preprocessed_asm.s"))); 42 | ``` 43 | 44 | # 詳細設計 45 | [詳細設計]: #詳細設計 46 | 47 | 見上文所述,沒有要多補充的。巨集(macro)會直接映射到 LLVM 的 `module asm`。 48 | 49 | # 缺點 50 | [缺點]: #缺點 51 | 52 | 像`asm!`一樣,這個功能取決於 LLVM 的集成組合語言 53 | 54 | # 替代方案 55 | [替代方案]: #替代方案 56 | 57 | 包含外部組合語言的現有方式是使用 `build.rs` 中的 gcc 編譯組合語言檔案,並將其連結到 Rust 程式中作為靜態庫。 58 | 59 | 對於完全以組合語言寫入的函式的替代方案是添加一個 [`#[naked]` function attribute](https://github.com/rust-lang/rfcs/pull/1201). 60 | 61 | # 未解決問題 62 | [未解決問題]: #未解決問題 63 | 64 | 無 65 | -------------------------------------------------------------------------------- /text/1598-generic_associated_types.md: -------------------------------------------------------------------------------- 1 | - Feature Name: generic_associated_types 2 | - Start Date: 2016-04-29 3 | - RFC PR: [rust-lang/rfcs#1598](https://github.com/rust-lang/rfcs/pull/1598) 4 | - Rust Issue: [rust-lang/rust#44265](https://github.com/rust-lang/rust/issues/44265) 5 | - Translators: [@wusyong](https://github.com/wusyong) 6 | - Commit: [a7cd910](https://github.com/rust-lang/rfcs/commit/a7cd91048eea3d7ae83bec20446e62bad0c45381) 7 | - Updated: 2022-11-02 8 | 9 | # 總結 10 | [summary]: #總結 11 | 12 | 讓型別建構子(type constructor)能與特徵(trait)相互關聯。在 Rust 使用者最想要的功能中,有個通常稱為「高階種類型別(higher-kinded types)」的功能,而這是達成這項目標的其中一步。關聯型別建構子(associated type constructors)這項特定的功能可以解決高階種類其中一項最普遍的使用情境,比起其他形式的高階種類多型(polymorphism)更容易擴充至原有的型別系統,而且能兼容未來可能所導入的更複雜形式的高階種類多型。 13 | 14 | # 動機 15 | [motivation]: #動機 16 | 17 | 讓我們先考慮以下的特徵範例來展示可能的動機: 18 | 19 | ```rust 20 | trait StreamingIterator { 21 | type Item<'a>; 22 | fn next<'a>(&'a mut self) -> Option>; 23 | } 24 | ``` 25 | 26 | 這樣的特徵是很實用的,它能提供一種疊代器(Iterator)所回傳的數值能附有一個生命週期,且能與傳至 `next` 的引用所擁有的生命週期綁定。此特徵最明顯的使用情境就是對一個向量(vector)所產生的疊代器,在每次疊代時都能產生重疊(overlapping)且可變(mutable)的子切片(subslice)。使用標準的 `Iterator` 介面的話,這樣的實作是無效的,因為每個切片必須活得與整個疊代器一樣長,而非只是透過 `next` 借用的時間而已。 27 | 28 | 這樣的特徵無法用現在的 Rust 表現出來,因為它需要依賴某種形式的高階種類多型。本 RFC 將擴充 Rust 來確保此特定形式的高階種類多型,也就是我們在此所稱作的關聯型別建構子。此功能有各種應用場景,但最主要的應用都還是與 `StreamingIterator` 類似:定義的特徵所持有的型別能附有一個生命週期,並與接收型別所借用的生命週期相互關聯。 29 | 30 | # 設計細節 31 | [design]: #設計細節 32 | 33 | ## 背景:什麼是種類(Kind)? 34 | 35 | 「高階種類型別」算是比較籠統的詞彙,容易與其他語言功能混淆,而造成我們想表達的概念不精確。所以本 RFC 將簡單介紹種類的概念。種類通常被稱為「型別的型別」,但這樣的解釋對於想理解的人幫助並不大,反而對已經了解這些概念的人才說得通。所以讓我們嘗試用型別比喻來了解種類。 36 | 37 | 在型別完善的語言中,每個表達式(expression)都有個型別。許多表達式都有所謂的「基本型別」,也就是語言中的原生型別而且無法用其他型別表達。在 Rust 中,`bool`、`i64`、`usize` 與 `char` 都是基本型別的明顯例子。另一方面,也就會有型別會是由其他型別組成,函式就是屬於這樣的例子。讓我們看看下面這個簡單的函式: 38 | 39 | ```rust 40 | fn not(x: bool) -> bool { 41 | !x 42 | } 43 | ``` 44 | 45 | `not` 的型別為 `bool -> bool`(抱歉這邊開始會使用些與 Rust 不大一樣的語法),而 `not(true)` 的型別則是 `bool`。注意這樣的不同點正是了解高階種類的關鍵。 46 | 47 | 在種類的分析中,`bool`、`char`、`bool -> bool` 等等型別都有 `type` 這種種類。每個型別都有 `type` 種類。然而 `type` 只是基本種類,就像 `bool` 只是個基本型別,我們能寫出更複雜的種類像是 `type -> type`,這樣的種類其中一個範例就是 `Vec` 能接受一個型別然後產生出一個型別。而 `Vec` 的種類與 `Vec` 的種類(也就是 `type`)之間的差異,就相對應於 `not` 與 `not(true)` 之間的差異。另外注意 `Vec` 的種類和 `Vec` 一樣仍然是 `type`。盡管 `T` 是個型別參數,`Vec` 還是接收了一個型別,就像 `not(x)` 的型別仍然是 `bool`,盡管 `x` 是個變數一樣。 48 | 49 | Rust 相對不尋常的特色是它有**兩種**基本種類,相較於許多語言處理高階種類時只會有一種種類 `type`。Rust 的另一個基本種類就是生命週期參數。如果你有個型別像是 `Foo<'a>`,`Foo` 的種類就是 `lifetime -> type`。 50 | 51 | 而高階種類當然也可以接受多重引數,`Result` 的種類就是 `type, type -> type`,而 `vec::Iter<'a, T>` 中的 `vec::Iter` 則會有 `lifetime, type -> type` 這樣的種類。 52 | 53 | 高階種類的表達通常就稱為「型別運算子(type operators)」,運算出一個型別的型別運算子就稱作「型別建構子(type constructors)」。也有其他型別運算子能運算出其他型別運算子,而且還有更高階的型別運算子能接收型別運算子作為引數,讓它們的種類變成像是 `(type -> type) -> type`。本 RFC 將不會討論這些更進階的範疇。 54 | 55 | 本 RFC 更明確的目標就是要讓型別建構子可以與特徵相互關聯,就像你現在的特徵能與函式、型別與 consts 相互關聯一樣。型別建構子還有包含其他形式的多型,像是對型別建構子實作特徵,而非只是型別而已。但這些都不包含在本 RFC 內。 56 | 57 | ## 關聯型別建構子的功能 58 | 59 | ### 宣告與賦值給關聯型別建構子 60 | 61 | 本 RFC 將提供個非常簡單的語法來定義關聯型別建構子,它會與建立型別建構子的別名用的語法非常相似。使用此語法的目的就是為了避免讓尚未能理解高階種類的使用者使用困難。 62 | 63 | ```rust 64 | trait StreamingIterator { 65 | type Item<'a>; 66 | } 67 | ``` 68 | 69 | 這樣能清楚表達關聯項目 `Item` 是一個型別建構子,而非只是個型別,因為它有個型別參數附加在旁。 70 | 71 | 與關聯型別一樣,關聯型別建構子可以用在界限內: 72 | 73 | ```rust 74 | trait Iterable { 75 | type Item<'a>; 76 | type Iter<'a>: Iterator>; 77 | 78 | fn iter<'a>(&'a self) -> Self::Iter<'a>; 79 | } 80 | ``` 81 | 82 | 此界限套用到型別建構子的「輸出」,然後參數會被視為高階參數。也就是說上述的界限幾乎等於對特徵加上這樣的界限: 83 | 84 | ```rust 85 | for<'a> Self::Iter<'a>: Iterator> 86 | ``` 87 | 88 | 在 `impl` 中賦值給關聯型別建構子的語法與賦值給關聯型別的非常接近: 89 | 90 | ```rust 91 | impl StreamingIterator for StreamIterMut { 92 | type Item<'a> = &'a mut [T]; 93 | ... 94 | } 95 | ``` 96 | 97 | ### 使用關聯型別建構子建立型別 98 | 99 | 一旦特徵擁有關聯型別建構子時,它就能套用在作用域中的任何參數或具體項。這能同時用在特徵的本體內與本體外,使用的語法就與使用關聯型別類似。以下是一些範例: 100 | 101 | ```rust 102 | trait StreamingIterator { 103 | type Item<'a>; 104 | // 在特徵內將生命週期 `'a` 套用至 `Self::Item`。 105 | fn next<'a>(&'a self) -> Option>; 106 | } 107 | 108 | struct Foo { 109 | // 在特徵外將實際生命週期套用至建構子。 110 | bar: ::Item<'static>; 111 | } 112 | ``` 113 | 114 | 關聯型別建構子也能用來產生其他型別建構子: 115 | 116 | ```rust 117 | trait Foo { 118 | type Bar<'a, 'b>; 119 | } 120 | 121 | trait Baz { 122 | type Quux<'a>; 123 | } 124 | 125 | impl Baz for T where T: Foo { 126 | type Quux<'a> = ::Bar<'a, 'static>; 127 | } 128 | ``` 129 | 130 | 最後,關聯型別建構子的生命週期和其他型別建構子的一樣能被省略。加上生命週期省略的話,`StreamingIterator` 的完整定義就可以是: 131 | 132 | ```rust 133 | trait StreamingIterator { 134 | type Item<'a>; 135 | fn next(&mut self) -> Option; 136 | } 137 | ``` 138 | 139 | ### 在界限中使用關聯型別建構子 140 | 141 | 對於參數的型別是由特徵的關聯型別建構子產生的話,使用者可以用高階特徵界限(Higher-Rank Trait Bounds)來綁定。無論是對型別相等的界限或是對此種類的特徵界限都是有效的: 142 | 143 | ```rust 144 | fn foo StreamingIterator=&'a [i32]>>(iter: T) { ... } 145 | 146 | fn foo(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { ... } 147 | ``` 148 | 149 | 本 RFC 並沒有提出任何允許型別建構子自己綁定的形式,無論是相等界限或特徵界限(當然特徵界限是不可能的)。 150 | 151 | ## 型別引數的關聯型別建構子 152 | 153 | 本 RFC 目前所有的範例都專注在生命週期引數的關聯型別建構子。但是本 RFC 一樣有提出導入型別的關聯型別建構子: 154 | 155 | ```rust 156 | trait Foo { 157 | type Bar; 158 | } 159 | ``` 160 | 161 | 本 RFC **並沒有**打算讓高階特徵界限能進一步接收型別引數,因為這會讓它們更難清楚地表達語義。我們的確有這樣的擴充需求,但這超出本 RFC 的範疇。 162 | 163 | 型別引數可以透過「Family」模式來達成其他形式的高階種類多型。舉例來說,使用 `PointerFamily` 特徵可以將 Arc 與 Rc 抽象出來: 164 | 165 | ```rust 166 | trait PointerFamily { 167 | type Pointer: Deref; 168 | fn new(value: T) -> Self::Pointer; 169 | } 170 | 171 | struct ArcFamily; 172 | 173 | impl PointerFamily for ArcFamily { 174 | type Pointer = Arc; 175 | fn new(value: T) -> Self::Pointer { 176 | Arc::new(value) 177 | } 178 | } 179 | 180 | struct RcFamily; 181 | 182 | impl PointerFamily for RcFamily { 183 | type Pointer = Rc; 184 | fn new(value: T) -> Self::Pointer { 185 | Rc::new(value) 186 | } 187 | } 188 | 189 | struct Foo { 190 | bar: P::Pointer, 191 | } 192 | ``` 193 | 194 | ## 特徵界限與 where 195 | 196 | ### 關聯型別建構子的界限 197 | 198 | 關聯型別建構子的界限會被視為特徵自己的高階界限。這讓它們的行為能與一般關聯型別的界限一致。舉例來說: 199 | 200 | ```rust 201 | trait Foo { 202 | type Assoc<'a>: Trait<'a>; 203 | } 204 | ``` 205 | 206 | 就等同於: 207 | 208 | ```rust 209 | trait Foo where for<'a> Self::Assoc<'a>: Trait<'a> { 210 | type Assoc<'a>; 211 | } 212 | ``` 213 | 214 | ### 關聯型別上的 `where` 215 | 216 | 另一方面,在關聯型別上使用 where 則會要求每次使用關聯型別時都得滿足其限制。舉例來說: 217 | 218 | ```rust 219 | trait Foo { 220 | type Assoc where Self: Sized; 221 | } 222 | ``` 223 | 224 | 每次呼叫 `::Assoc` 時都需要證明 `T: Sized`,就像其他場合需要證明有符合 impl 的界限一樣。 225 | 226 | (@nikomatsakis 相信在某些場合的 where 會需要關聯型別建構子指明處理生命週期。實際細節將不會包含在本 RFC,因為這在實際實作時才會更清楚。) 227 | 228 | ## 在其他高階種類多型之前只實作此功能的優點 229 | 230 | 此功能並非完整的高階種類多型,也無法允許那些在 Haskell 中熱門的抽象形式,但它能提供 Rust 獨一無二的高階種類多型使用情境。像是 `StreamingIterator` 與各式集合的特徵。這很可能也會是許多使用者最需要用到的功能,讓還不知道高階種類的人也能輕易直觀地使用。 231 | 232 | 此功能會有些麻煩的實作挑戰,但也避免了其他所有高階種類多型所需的功能像是: 233 | 234 | * 定義高階種類特徵 235 | * 對型別運算子實作高階種類 236 | * 高階型別運算子 237 | * 高階種類特徵綁定的型別運算子參數 238 | * 型別運算子參數套用至一個給予的型別或型別參數 239 | 240 | ## 建議語法的優點 241 | 242 | 建議語法的優點在於它建立在已經存在的語法上,型別建構子已經能在 Rust 中使用相同的語法建立別名。雖然型別別名在型別解析上沒有多型的意義,對於使用者來說他們非常類似於關聯型別。此語法的目標就是要讓許多使用者能夠輕易使用具有關聯型別建構子的型別,就算他們沒有察覺到這和高階種類有關。 243 | 244 | # 我們如何教導這個 245 | [how-we-teach-this]: #我們如何教導這個 246 | 247 | 在本 RFC 我們使用「關聯型別建構子」,這是因為這在 Rust 社群中已經很常拿來使用作為此功能的討論了。但這並沒有很輕易地表達此概念,尤其光是型別理論中的「型別建構子」就已經足以讓人望之卻步了,多數使用者可能並不熟悉這樣的詞彙。 248 | 249 | 在接受本 RFC 後,我們應該開始稱呼此概念為「泛型關聯型別(generic associated types)」就好,現在的關聯型別還無法使用泛型。在此 RFC 後,這就化為可能了。與其教導這是一個不同的功能,不如介紹說這會是關聯型別的進階使用情境。 250 | 251 | 像是「Family」特徵的模式也需要同時教導,我們可能會寫進書裡或是某種形式的文件就好,像是網誌文章等。 252 | 253 | 這也可能會增加使用者使用高階特徵界限的頻率,我們可能會需要挹注更多資源在高階特徵界限上。 254 | 255 | # 缺點 256 | [drawbacks]: #缺點 257 | 258 | ## 增加語言複雜度 259 | 260 | 這會對語言增加一定的複雜度,型別建構子將能夠多型產生,而且型別系統也需要幾個擴充,這都讓實作更加複雜。 261 | 262 | 除此之外,雖然語法設計旨在易於學習此功能,但它更有可能產生模糊空間讓使用者意外使用到,而不是他們原本想寫的,這就像 `impl .. for Trait` 和 `impl .. for T where T: Trait` 之間產生的混淆。舉例來說: 263 | 264 | ```rust 265 | // 使用者其實想寫這樣 266 | trait Foo<'a> { 267 | type Bar: 'a; 268 | } 269 | 270 | // 但他們寫成這樣 271 | trait Foo<'a> { 272 | type Bar<'a>; 273 | } 274 | ``` 275 | 276 | ## 並非完整的「高階種類型別」 277 | 278 | 本 RFC 並沒有加入當大家講到高階種類型別時的所有功能。舉例來說,它並沒有辦法提供特徵像是 `Monad`。有些人傾向於一次實作完所有這些功能。然而,此功能是能夠兼容於其他形式的高階種類多型的,且並沒有將他們的可能性排除掉。事實上,它還解決了一些會影響其他高階種類形式的實作細節,為它們開拓了可能的路徑,像是 Partial Application。 279 | 280 | ## 語法與其他形式的高階種類多型都不一樣 281 | 282 | 雖然建議的語法非常近似於關聯型別與型別別名的語法,其他形式的高階種類多型可能無法使用相同的語法來表達。基於此原因,定義關聯型別建構子的語法可能會與,舉例來說,對特徵實作型別建構子的語法不同。 283 | 284 | 不過這些其他形式的高階種類多型也將會依到它們實際的功能來決定語法。要設計一個未知功能的語法是非常困難的。 285 | 286 | # 替代方案 287 | [alternatives]: #替代方案 288 | 289 | ## 進一步強化高階特徵界限而不是關聯型別建構子 290 | 291 | 其中一種替代方案是強化高階特徵界限,有可能是加入一些省略規則,讓它們更容易使用。 292 | 293 | 目前可能的 `StreamingIterator` 也許能被定義成這樣: 294 | 295 | ```rust 296 | trait StreamingIterator<'a> { 297 | type Item: 'a; 298 | fn next(&'a self) -> Option; 299 | } 300 | ``` 301 | 302 | 這樣你就可以綁定型別成 `T: for<'a> StreamingIterator<'a>` 來避免每次出現 `StreamingIterator` 時都會被生命週期感染。 303 | 304 | 然而這樣僅避免了 `StreamingIterator` 的傳染性,只能用在一些關聯型別建構子能夠表達的型別,而且這更像是針對限制下的權變措施,而非等價的替代方案。 305 | 306 | ## 對關聯型別建構子施加限制 307 | 308 | 我們常稱呼的「完整高階種類多型」能允許使用型別建構子作為其他型別建構子的輸入參數,換句話說就是高階型別建構子。在沒有任何限制的情況下,多重參數的高階型別建構子會對型別介面帶來嚴重的問題。 309 | 310 | 舉例來說,當你嘗試推導型別時,而且你知道你有個建構子的形式為 `type, type -> Result<(), io::Error>`,如果沒有任何限制的話,我們很難判斷此建構子到底是 `(), io::Error -> Result<(), io::Error>` 還是 `io::Error, () -> Result<(), io::Error>`。 311 | 312 | 有鑑於此,將高階種類多型視為第一公民的語言通常都會對這些高階種類施加限制,像是 Haskell 的柯里(currying)規則。 313 | 314 | 如果 Rust 也要採納高階型別建構子的話,我們需要對型別建構子可以接收的種類施加類似的限制。但關聯型別建構子已經是一種別名,其自然就囊括了實際型別建構子的結構。換句話說,如果我們想要使用關聯型別建構子作為高階型別建構子的引數,我們需要將那些限制施加於**所有**的關聯結構建構子。 315 | 316 | 我們已經有一份我們認為必要且足夠的限制清單,更多細節可以在 nmatsakis 的[網誌文章](http://smallcultfollowing.com/babysteps/blog/2016/11/09/associated-type-constructors-part-4-unifying-atc-and-hkt/)找到: 317 | 318 | * 關聯型別建構子的每個引數都必須被套用到 319 | * 它們必須以它們在關聯型別建構子出現的相同順序來套用 320 | * 它們必須套用恰好一次而已 321 | * 它們必須是建構子的最左引數 322 | 323 | 這些限制已經非常有建設性了,我們已經知道有一些關聯型別建構子的應用會被受限於此,像是 `Iterable` 給 `HashMap` 的定義(項目 `(&'a K, &'a V)` 要套用兩次生命週期)。 324 | 325 | 有鑑於此,我們決定**不要**對所有關聯型別建構子施加這些限制。這代表如果到時候有高階型別建構子加入語言中的話,它們將無法將關聯型別建構子作為引數。然而,這其實能透過新型別(newtypes)來滿足限制,舉例來說: 326 | 327 | ```rust 328 | struct IterItem<'a, I: Iterable>(I::Item<'a>); 329 | ``` 330 | 331 | # 未解決問題 332 | [unresolved]: #未解決問題 333 | -------------------------------------------------------------------------------- /text/2000-const-generics.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `const_generics` 2 | - Start Date: 2017-05-01 3 | - RFC PR: [rust-lang/rfcs#2000](https://github.com/rust-lang/rfcs/pull/2000) 4 | - Rust Issue: [rust-lang/rust#44580](https://github.com/rust-lang/rust/issues/44580) 5 | - Translators: [[@yanganto](https://github.com/yanganto)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-tw/rfcs-tw/blob/725bf172f383a21cf74189feb5073705778d2206/text/2000-const-generics.md) 7 | - Updated: 2021-03-26 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 允許所有型別(type)以作為常數值(constant values)的形式成為泛型(generic),這能讓使用者用 `impl` 寫出所有陣列型別(array types)。 13 | 14 | # 動機 15 | [動機]: #動機 16 | 17 | 現在的 Rust 有一種將常數參數化(parametric)型別︰內建的陣列型別 `[T; LEN]`。然而,常數泛型(const generics)並非第一級別的功能,使用者無法自己定義將常數值作爲泛型的型別,且不能對所有的陣列實作特徵(traits)。 18 | 19 | 在此限制下,標準庫僅包含長度最多到 32 的陣列實作,而陣列也因此被視爲語言的第二級別的功能。即使在長度可靜態已知的情形下,也大多調配於堆(heap)上成為向量(vector)而非使用陣列型別,因而造成一定程度上的效能取捨。 20 | 21 | 常數參數也能讓使用者更自然地指定一個泛型變體(variant), 且相比於型別更能確切地反應其值。舉例來說,基於某些因素有個型別用一個名稱來作為參數的話,使用 `&'static str` 來提供其名稱會比(藉由相關的常數或是函式提供的)單元型別(unit type)的形式還來的合理,這能簡化 API。 22 | 23 | 總結來說,常數可作爲參數使用,讓編譯器在型別檢查時期能確定這些數值。藉由限制哪些數值是否有實作過特定的特徵,孤兒規則(orphan rules)可以確保一個 crate 僅使用部份安全的數值。(例如有關密碼學的相關函式庫) 24 | 25 | 26 | # 設計細節 27 | [設計]: #設計細節 28 | 29 | 現今 Rust 中的型別有兩種參數化的形式︰型別與生命周期。如同在編譯時能推導出這些數值,我們將額外的允許型別將數值參數化。一個常數參數必為單一、特定型別,且可以有效的替換為編譯時期計算出的任意值且該型別將符合本 RFC 以下列出的要求。 30 | 31 | (為明確在此 RFC 闡明哪些表達式可在編譯時評估,在之後的範例中,我們假設整數及其基本算數操作可以在編譯時計算。) 32 | 33 | ## 詞彙表 34 | 35 | * 常數(constant, const value)︰一個在編譯時期可以保證完全評估的 Rust 數值。不同於靜態數值(static),常數將在其使用的位置 inline 而非存在於編譯好的二進制檔案的資料區段。 36 | 37 | * 常數參數、泛型常數(const parameter, generic const)︰一個由型別或函式抽象而得到的常數。此常數是具體型別的輸入值,例如一個靜態陣列的長度。 38 | 39 | * 關聯常數(associated const)︰一個由特徵關聯而得到的常數,其相似於關聯型別。不同於常數參數,關聯常數是由型別所決定的。 40 | 41 | * 常數變數(const variable)︰相比於具體常數,常數參數或關聯常數皆為常數變數。在單態化(monomorphization)之前,一個常數在上下文中是未確定的。 42 | 43 | * 具體常數(concrete const)︰相比於常數變數,一個在上下文中已知且單一值的常數。 44 | 45 | * 常數表達式(const expression)︰一個用於評估一個常數的表達式。此可為一個身份表達式或一個在 Rust 常數系統中可以評估出的更複雜的表達式。 46 | 47 | * 抽象常數表達式(abstract const expression)︰一個包含常數變數的表達式。(因此在單態化結束前其值是無法評估的) 48 | 49 | * 常數投影(const projection)︰抽象常數表達式的數值(其在泛型上下文中因缺乏所依賴的常數變數而無法被定義) 50 | 51 | * 身份表達式(identity expression)︰一個在不以其範圍內的名稱進行置換則無法評估的表達式。此包括了所有的文字及身份(ident),例如,`3`、`"Hello, world"`、`foo_bar`。 52 | 53 | ## 宣告常數參數 54 | 55 | 在型別參數宣告的任何序列中都可以宣告常數參數(例如在一個型別的定義中或是在 `impl` 的標頭或區塊(block)中)。常數參數的格式爲 `const $ident: $ty`。 56 | 57 | ```rust 58 | struct RectangularArray { 59 | array: [[T; WIDTH]; HEIGHT], 60 | } 61 | ``` 62 | 63 | 這些宣告的身份(ident)就是常數參數(在本 RFC 內文中可替稱作「常數變數」)的名稱,而且所有數值必定其推導的型別。本 RFC 後文將說明能被推導的型別有哪些限制。 64 | 65 | 宣告的常數變數範圍在該項目(type、impl、function、method、…等等)的整體範圍中。 66 | 67 | ## 套用常數作為參數 68 | 69 | 任何可推導出該常數參數型別的常數表達式都可作爲參數。除了陣列外,當套用一個表達式作為常數參數且該表達式並非身份表達式時,該表達式必須包含在區塊內 。此語法上的限制是必要的,以避免在型別中解析表達式需要無限地向前展望(lookahead)。 70 | 71 | 72 | ```rust 73 | const X: usize = 7; 74 | 75 | let x: RectangularArray; 76 | let y: RectangularArray; 77 | ``` 78 | 79 | ### 陣列 80 | 陣列語法中有一個特別的構造語法:`[T; CONST]`。在陣列中大括號不需要存在於任何的常數表達式中,`[i32; N * 2]` 是一個合理的型別。 81 | 82 | ## 何時可使用常數變數 83 | 84 | 常數變數可使用於下列上下文之任一項中︰ 85 | 86 | 1. 用於任一型別中的常數,該型別是待定項目之簽章(signature)的一部份:`fn foo(arr: [i32; N])` 87 | 2. 用於定義相關常數的常數表達式或相關型別之參數的一部份 88 | 3. 在項目中任何函式的內部之任何運行時表達式中的數值 89 | 4. 在項目中任何函式內部用於任何型別的參數,例如在 `let x: [i32; N]` 或 `<[i32; N] as Foo>::bar()`。 90 | 5. 在項目中任何欄位之型別的一部分(如 `struct Foo([i32; N]);`) 91 | 92 | 在一般情況下,常數變數可以用做常數。但有一個明顯的例外是,常數變數不可用於常數、靜態型別、函式、或函式內的型別的建構子,意即下面的例子是不合規的。 93 | 94 | ```rust 95 | fn foo() { 96 | const Y: usize = X * 2; 97 | static Z: (usize, usize)= (X, X); 98 | 99 | struct Foo([i32; X]); 100 | } 101 | ``` 102 | 103 | 這樣的限制就如同型別型別不能用在函式本體中建造的型別一樣。所有的宣告雖然專用於該項目,但也必需獨立於它,且不能有任何在其範圍內的參數。 104 | 105 | ## 兩個常數的型別相等性之相等原則 106 | 107 | 在統一且重疊的檢查期間,何時兩個型別是否相等是關重要的。因為型別現在可依賴於常數了,所以我們必須定義我們如何比較兩個常數表達式的相等性。 108 | 109 | 在大多數情況下,兩個常數的相等性會如同你的預期,如果兩個常數彼此相等,則它們相等。但是仍會有一些特殊的狀況。 110 | 111 | ### 結構相等性 112 | 113 | 常數相等性的定義是根據 [RFC 1445][1445] 解釋的結構相等性所定義。只有在型別有「結構相符」的特性下,該結構才可用做常數參數。舉例來說,浮點數就會被排除在外。 114 | 115 | 在最終解決方案出來前,結構相符這樣的性質應作為權宜之計。無論變數參數採用什麼解決方案,確保相等是具有反射性(reflexive)對於型別相等而言是重要的,這樣才能使得型別始終相同。(浮點數相等的標准定義是不具反射性) 116 | 117 | 這可能會與未來進行配對(match)的定義而有所不同,但是配對和常數參數不一定得使用相同的相等性定義,現今所使用的相等性定義已經足以滿足我們的目的。 118 | 119 | 因為常數必須具有結構相符的性質,並且此性質無法強制轉成一個型別變數,因此我們無法定義一個常數參數是從其他型別變數推導來的。(意即 `Foo `不是合規的) 120 | 121 | ### 兩個抽像表達式的相等性 122 | 123 | 在比較兩個抽象常數表達式(即依賴於變數的表達式)的相等性時,我們無法比較其值的相等性,因其值是由常數變數所決定的,而在在單態化之前是未知的。 124 | 125 | 基於這個原因,我們至少會將常數表達式的返回值當作投影(projections)。雖然其值仍保持未知,我們仍藉由輸入值來決定其值,這是作法相同於現在我們處理關聯型別(associated types)的作法。此作法我們將稱為常數投影(const projection),對另一個同型別的常數而言,我們永遠無法確定其相等性。 126 | 127 | 每個常數表達式都會生成一個新的投影,該投影本質上是匿名的。不可能使兩個匿名投影一致(想像兩個關聯型別的泛型 `T :: Assoc` 和 `T :: Item`,你無法證明或否定它們是否為同一型別)。因此,除非它們從字面上使用完完全全相同的文字,否則常數表達式在 AST 節點中是彼此不一樣的。這意味著 `N + 1` 的一個實例不會與另一個 `N + 1` 的實例在型別相同。 128 | 129 | 更清楚來說,以下是無法通過型別檢查的,因為 `N + 1` 會是兩種不同的型別: 130 | 131 | ```rust 132 | fn foo() -> [i32; N + 1] { 133 | let x: [i32; N + 1] = [0; N + 1]; 134 | x 135 | } 136 | ``` 137 | 138 | 但如果這樣寫的話,它將只有一個型別: 139 | 140 | ```rust 141 | type Foo = [i32; N + 1]; 142 | 143 | fn foo() -> Foo { 144 | let x: Foo = Default::default(); 145 | x 146 | } 147 | ``` 148 | 149 | #### 未來的擴展性 150 | 151 | 未來的某一天我們可以利用一些操作的基本性質(例如加法和乘法的可交換性),而對常數投影的相等性做出更聰明的判斷。但是,本 RFC 並不打算在此提案中建造這些可能性,而是打算留給未來的 RFC 決定。 152 | 153 | ## 常數變數的特化(Specialization) 154 | 155 | 定義常數參數特化的順序也是很重要的。為此,將字面上的定義必需比其他表達式更具體,否則表達式定義的順序上也會產生不確定性。 156 | 157 | 正如我們有朝一日可以在常數投影上支援更進階的相等性一樣,我們可以支援更進階的特化定義。例如,給定型別為 `(i32, i32)`,我們可以確定 `(0, PARAM2)` 比 `(PARAM1, PARAM2)` 更為具體;`(i32, U)` 比 `(T, U)` 更為具體。在未來的某天我們將也有可能在常數特化上支援多元交互(intersectional)和其他更進階的定義。 158 | 159 | # 我們如何教導這個 160 | [我們如何教導這個]: #我們如何教導這個 161 | 162 | 常數泛型是一個很龐大的功能,將需要大量的教育資源,這將會需要寫在在書本跟參考文件中,且可能會在書中有獨立的章節。常數泛型的文件化過程本質上將會是一個大工程。 163 | 164 | 然而,常數泛型應該被視為進階功能,並且在使用 Rust 的初期,我們應該不會向新手介紹這些內容。 165 | 166 | # 缺點 167 | [缺點]: #缺點 168 | 169 | 此功能由於允許型別由常數決定,將為型別系統增加了大量的複雜性。它需要抽象變數相等性的確定規則,這出現了很多令人意外的的特殊情況。它增加了 Rust 的很多語法。如果我們不要採用此功能,Rust 肯定會更簡單。 170 | 171 | 然而,我們已經引入了一種由常數確定的型別(數組型別)。泛型化的功能似乎是必然的,甚至是不可避免的,鑑於此,我們應該盡早決定。 172 | 173 | # 替代方案 174 | [替代方案]: #替代方案 175 | 176 | 除了暫緩執行或不執行外,並沒有真的替代方案。 177 | 178 | 我們可以限制常數泛型為 `usize` 型別,但這不會讓實作更為簡單。 179 | 180 | 我們可以對常數相對性的複雜概念更積極地發展,但這會使實作比上述說明得更加複雜。 181 | 182 | 我們可以選擇稍微不同的語法,例如將在常數跟型別間加上分號。 183 | 184 | # 未解決問題 185 | [未解決問題]: #未解決問題 186 | 187 | - 一致的抽象常數表達式︰本 RFC 盡可能最大限度地減少抽象常數表達式的統合性上的處理,從本質上來說,並沒有使其一致。這可能造成無法接受的使用者體驗的不穩定,而我們想要實作一些更進些的統合性之前穩定此功能。 188 | - 常數表達式之正確格式︰只有在單態化過程中,程式不會恐慌(panic)的情況下,此型別才能視爲正確格式。這對於溢出和超出範圍的陣列存取來說很棘手。然而,我們實際上只能確保在函式的簽章中表達式常數的格式進行正確性約束。目前尚不清楚有關在函式中抽象常數表達式之格式正確性的處理方式,也因此使實作推遲。 189 | - 排序與預設參數︰所有常數參數是否將排在最後,或者將它們與型別混合嗎?具有預設值的參數是否須在沒有預設值之後?這些決定推遲到實作語法的討論中。 190 | 191 | [1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md 192 | -------------------------------------------------------------------------------- /text/2342-const-control-flow.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `const-control-flow` 2 | - Start Date: 2018-01-11 3 | - RFC PR: [rust-lang/rfcs#2342](https://github.com/rust-lang/rfcs/pull/2342) 4 | - Rust Issue: [rust-lang/rust#49146](https://github.com/rust-lang/rust/issues/49146) 5 | - Translators: [[@CYBAI](https://github.com/CYBAI)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/dfe697106478a52bddc000477e8cd0621bcc1a20/text/2342-const-control-flow.md) 7 | - Updated: 2020-09-05 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 透過此功能,可以在常數求值(const evaluation)中使用 `if` 及 `match` 並使他們被延遲求值。簡單來說,這項功能允許我們寫 `if x < y { y - x } else { x - y }`;即使當使用非負型別時在 `x < y` 的 `else` 分支會報溢位錯誤 (overflow error)。 13 | 14 | # 動機 15 | [動機]: #動機 16 | 17 | 在常數宣告中使用條件式對於建立像是 `NonZero::new` 的 `const fn` 及 直譯判定(interpreting assertions)來說很重要。 18 | 19 | # 教學式解說 20 | [教學式解說]: #教學式解說 21 | 22 | 如果你寫 23 | 24 | ```rust 25 | let x: u32 = ...; 26 | let y: u32 = ...; 27 | let a = x - y; 28 | let b = y - x; 29 | if x > y { 30 | // do something with a 31 | } else { 32 | // do something with b 33 | } 34 | ``` 35 | 36 | 這支程式永遠都會 panic(除非 `x` 和 `y` 同時是 `0`)因為不管是 `x - y` 或是 `y - x` 都會造成溢位。為了解決此問題,我們必須把 `let a` 及 `let b` 個別搬進 `if` 及 `else` 中。 37 | 38 | ```rust 39 | let x: u32 = ...; 40 | let y: u32 = ...; 41 | if x > y { 42 | let a = x - y; 43 | // do something with a 44 | } else { 45 | let b = y - x; 46 | // do something with b 47 | } 48 | ``` 49 | 50 | 當改用常數時,上面的寫法就會出現新問題: 51 | 52 | ```rust 53 | const X: u32 = ...; 54 | const Y: u32 = ...; 55 | const FOO: SomeType = if X > Y { 56 | const A: u32 = X - Y; 57 | ... 58 | } else { 59 | const B: u32 = Y - X; 60 | ... 61 | }; 62 | ``` 63 | 64 | `A` 和 `B` 會比 `FOO` 先被求值,因為常數在定義上就是「常數」,所以不應被求值順序影響。這項假設在有錯誤的情況下並不成立,因為錯誤屬於副作用(side effects),因此不純(pure)。 65 | 66 | 為了解決此問題,我們必須把中介常數消掉並改為直接對 `X - Y` 及 `Y - X` 求值。 67 | 68 | ```rust 69 | const X: u32 = ...; 70 | const Y: u32 = ...; 71 | const FOO: SomeType = if X > Y { 72 | let a = X - Y; 73 | ... 74 | } else { 75 | let b = Y - X; 76 | ... 77 | }; 78 | ``` 79 | 80 | # 技術文件式解說 81 | [技術文件式解說]: #技術文件式解說 82 | 83 | `if` 或是在 variant 沒有欄位的 enums 上做 `match` 會在 HIR -> MIR 階段時,被轉譯成 `switchInt` 終止器(terminator)。Mir 直譯器現在將會針對那些終止器求值(之前就可以了)。 84 | 85 | 在 variant 沒有欄位的 enums 上做 `match` 會被轉譯成 `switch`,表示他會被檢查 discriminant 或是在 packed enums(例如 `Option<&T>`)的情況會運算 discriminant(這個情況 discriminant 沒有特別的記憶體位址,但他會把所有的零視為 `None`,並把其他的值都當作 `Some`)。當進入 `match` 的分支時,匹配上的值基本上會被 transmute 成 enum 的 variant 型別,如此一來可以允許其他程式碼來存取該 enum 的欄位。 86 | 87 | # 缺點 88 | [缺點]: #缺點 89 | 90 | 這項功能容易造成任意「常數」值(如:`size_of::()` 或是特定的平台常數)編譯失敗。 91 | 92 | # 原理及替代方案 93 | [原理及替代方案]: #原理及替代方案 94 | 95 | ## 利用中介 const fns 來破壞立即常數求值(eager const evaluation) 96 | 97 | 如果寫成 98 | 99 | ```rust 100 | const X: u32 = ...; 101 | const Y: u32 = ...; 102 | const AB: u32 = if X > Y { 103 | X - Y 104 | } else { 105 | Y - X 106 | }; 107 | ``` 108 | 109 | `X - Y` 或是 `Y - X` 其中一方有可能會報錯,這時必須加入中介 const fn 110 | 111 | ```rust 112 | const X: u32 = ...; 113 | const Y: u32 = ...; 114 | const fn foo(x: u32, y: u32) -> u32 { 115 | if x > y { 116 | x - y 117 | } else { 118 | y - x 119 | } 120 | } 121 | const AB: u32 = foo(X, Y); 122 | ``` 123 | 124 | const fn 的 `x` 和 `y` 參數未知,無法做常數求值(const evaluate)。當提供此 const fn 參數並求值時,只會對相應的分支求值。 125 | 126 | # 未解決問題 127 | [未解決問題]: #未解決問題 128 | -------------------------------------------------------------------------------- /text/2394-async_await.md: -------------------------------------------------------------------------------- 1 | - Feature Name: async_await 2 | - Start Date: 2018-03-30 3 | - RFC PR: [rust-lang/rfcs#2394](https://github.com/rust-lang/rfcs/pull/2394) 4 | - Rust Issues: 5 | - [rust-lang/rust#50547](https://github.com/rust-lang/rust/issues/50547) 6 | - [rust-lang/rust#62290](https://github.com/rust-lang/rust/issues/62290) - #!feature(async_closure) 7 | - Translators:[[@FizzyElt](https://github.com/FizzyElt)] 8 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/6041dd9ff2ed97eff4e054117cc1581b8c45d8c1/text/2394-async_await.md) 9 | - Updated 2022-09-21 10 | 11 | # 概要 12 | [概要]: #summary 13 | 14 | 新增 async 和 await 語法,使撰寫程式碼操作 futures 更符合人因工程學。 15 | 16 | 另有一個[配套 RFC](2592-futures.md),用於向 libstd 和 libcore 新增一個小型 futures API。 17 | # 動機 18 | [動機]: #motivation 19 | 20 | 高效能網路服務經常使用非同步 IO,而不是阻塞式 IO,這樣在處理許多並行連線時更容易獲得更好的效能表現。 Rust 已經在網路服務領域得到了一些採用,我們希望透過使 Rust 中撰寫非同步網路服務更符合人因工程學,繼續支援這些使用者 - 並支援其他使用者採用。 21 | 22 | Rust 中非同步 IO 的發展經歷了多個階段。在 1.0 之前,我們嘗試在語言中內嵌綠色執行緒(green-threading) runtime。然而,事實證明這太過武斷了——因為它影響了每個用 Rust 撰寫的程式——並且在 1.0 之前不久就被刪除了。在 1.0 之後,非同步 IO 最初的重點是 mio 函式庫,它為 Linux、Mac OS 和 Windows 的非同步 IO 基元(primitive)提供了一個跨平台抽象。 2016 年年中,future crate 的引入產生了重大影響,它為非同步操作提供了一個方便且可共享的抽象。 tokio 函式庫提供了一個基於 mio 的事件循環,可以執行使用 future 介面實作的程式碼。 23 | 24 | 在獲得基於 future 的生態系統的經驗和使用者的回饋後,我們發現了某些人因工程學挑戰。使用需要在等待點(await point)之間共享的狀態,是非常不符合人因工程學的(需要 Arc 或 join chaining)雖然組合器通常比手動撰寫 future 更符合人因工程學,但它們仍然經常導致混亂的嵌套和 chained callbacks。 25 | 26 | 幸運的是,Future 抽象非常適合與一種語法糖一起使用——這個語法糖在許多具有非同步 IO 的語言中愈來愈常見:async 和 await 關鍵字。簡而言之,非同步函式回傳一個 future,而不是在呼叫時立即執行。在函式內部,可以使用 await 表達式等待其他 future,這使它們在輪詢 future 時讓出控制權。從使用者的角度來看,他們可以像在使用同步程式碼一樣使用 async/await,並且只需要標注其函式和調用。 27 | 28 | Async/await 和 futures 通常是非同步和並行的強大抽象,並且可能可以應用在非同步 I/O 空間以外的地方。我們今天遇到的案例通常與非同步 IO 相關,但透過引入一等公民語法和 libstd 的支援,我們相信更多不直接與非同步 IO 相關的 async 和 await 案例也會蓬勃發展。 29 | 30 | # 教學式解說 31 | [教學式解說]: #guide-level-explanation 32 | 33 | ## 非同步函式 34 | 35 | 對函式加上 `async` 關鍵字,使它們成為「非同步函式」: 36 | 37 | ```rust 38 | async fn function(argument: &str) -> usize { 39 | // ... 40 | } 41 | ``` 42 | 43 | 非同步函式的工作方式與普通函式不同。呼叫非同步函式時,它不會立即進入主體。相反,它執行實作 `Future` 特徵的匿名型別。當輪詢該 future 時,該函式被執行到它內部的下一個 `await` 或回傳點(請參閱接下來 await 語法部分)。 44 | 45 | 非同步函式是延遲計算的一種 - 在您開始輪詢函式回傳的 future 之前,函式本體中沒有任何內容被實際執行。例如: 46 | 47 | ```rust 48 | async fn print_async() { 49 | println!("Hello from print_async") 50 | } 51 | 52 | fn main() { 53 | let future = print_async(); 54 | println!("Hello from main"); 55 | futures::executor::block_on(future); 56 | } 57 | ``` 58 | 59 | 這將在印出 `"Hello from main"` 之前印出 `"Hello from print_async"`。 60 | 61 | `async fn foo(args..) -> T` 是 `fn(args..) -> impl Future` 型別的函式。回傳型別是編譯器產生的匿名型別。 62 | 63 | ### `async ||` closures 64 | 65 | 除了函式,非同步也可以應用在 closure 上面。與非同步函式一樣,非同步 closure 的回傳型別為 `impl Future`,而不是 `T`。當您呼叫該 closure 時,它會立即回傳一個 future,且不會執行任何程式碼(就像非同步函式一樣)。 66 | 67 | ```rust 68 | fn main() { 69 | let closure = async || { 70 | println!("Hello from async closure."); 71 | }; 72 | println!("Hello from main"); 73 | let future = closure(); 74 | println!("Hello from main again"); 75 | futures::block_on(future); 76 | } 77 | ``` 78 | 79 | 這將在印出 `"Hello from async closure."` 之前印出兩個 `"Hello from main"`。 80 | 81 | `async` closure 可以用 `move` 來捕捉它們包覆在 closure 內的變數的所有權。 82 | 83 | ## `async` 區塊 84 | 85 | 您可以使用 `async` 區塊直接將 future 建立為表達式: 86 | 87 | ```rust 88 | let my_future = async { 89 | println!("Hello from an async block"); 90 | }; 91 | ``` 92 | 93 | 這種形式幾乎等同於立即呼叫的 `async` closure。即是: 94 | 95 | ```rust 96 | async { /* body */ } 97 | 98 | // is equivalent to 99 | 100 | (async || { /* body */ })() 101 | ``` 102 | 103 | 除了像 `return`、`break` 和 `continue` 這樣的控制流程結構不允許在 `body` 中使用(除非它們出現在一個新的控制流上下文中,比如 closure 或 loop)。 尚未確定 `?` 運算子和提早回傳(early return)在非同步區塊運作的方式(請參閱未解決的問題)。 104 | 105 | 與 `async` closure 一樣,`async` 區塊可以加入 `move`,以捕捉區塊內所包覆的變數的所有權。 106 | 107 | ## 編譯器內嵌的 `await!` 108 | 109 | 編譯器加入了一個名為 `await!` 的內建函式。`await!` 可用於「暫停」future 的計算,將控制權交還給呼叫者。`await!` 接受任何實作 `IntoFuture` 的表達式,並計算為此 future 所傳入的泛型型別(如下面範例的 `Output`)之值。 110 | 111 | ```rust 112 | // future: impl Future 113 | let n = await!(future); 114 | ``` 115 | 116 | await 展開的程式碼,會重複在接收到的 future 上呼叫 `poll`:`poll` 回傳 `Poll::Pending` 時讓出 (yield) 函式的控制權,並在最終回傳 `Poll::Ready` 時取得項目的值。 117 | 118 | `await!` 只能在非同步函式、closure 或區塊內使用,除此之外使用它都是錯誤的。 119 | 120 | (`await!` 是編譯器的內建函式,為以後確定其確切語法保留彈性空間。詳細資訊請參閱〈未解決的問題〉部分。) 121 | 122 | # 技術文件式解說 123 | [技術文件式解說]: #reference-level-explanation 124 | 125 | ## 關鍵字 126 | 127 | `async` 和 `await` 在 2018 版本中都成為關鍵字。 128 | 129 | ## `async` 函式、closure、區塊的回傳型別 130 | 131 | 非同步函式的回傳型別是編譯器生成的唯一匿名型別,和 closure 的型別類似。你可以把這種型別想像成一個枚舉,函式的每個「yield point」都是一個變體——開頭、await 表達式和每一次的回傳。每個變體都會儲存需要保存的狀態,以便從該 yield point 恢復控制。 132 | 133 | 呼叫函式時,此匿名型別以其初始狀態回傳,其中包含此函式的所有引數。 134 | 135 | ### 特徵綁定 136 | 137 | 匿名回傳型別實作 `Future`,`Item` 為它的回傳型別。輪詢它會推進函數的狀態,當它在 `await` 狀態時會返回 `Pending`;當它在 `return` 狀態時則返回 `Ready`。任何在它已經回傳 `Ready` 一次後對其嘗試進行輪詢都將造成恐慌。 138 | 139 | 匿名回傳型別對 `Unpin` 特徵有一個相反的實作,即 `impl !Unpin`。這是因為 future 可能有內部引用,這意味著它永遠不需要被移動。 140 | 141 | ## 匿名 future 的生命週期捕捉 142 | 143 | 該函式的所有輸入生命週期都在非同步函式回傳的 future 捕捉,因為它將函式的所有引數儲存在其初始狀態(可能還有以後的狀態)。也就是說,給定這樣的函數: 144 | 145 | ```rust 146 | async fn foo(arg: &str) -> usize { ... } 147 | ``` 148 | 149 | 它具有與此等效的類型簽名: 150 | 151 | ```rust 152 | fn foo<'a>(arg: &'a str) -> impl Future + 'a { ... } 153 | ``` 154 | 155 | 這與 `impl Trait` 的預設值不同,它不捕捉生命週期。這就是為什麼回傳類型是 `T` 而不是 `impl Future` 的一個重要部分。 156 | 157 | ### 「初始化」模式 158 | 159 | 有時會出現的一種模式是 future 有一個「初始化」步驟,應該在其建立期間被執行。這在處理資料轉換和臨時借用時很有用。因為 async 函式在您輪詢它之前不會開始計算,並且它會捕捉其引數的生命週期,因此這種模式不能直接用 `async fn` 表示。 160 | 161 | 其中一個解決辦法,是撰寫一個回傳 `impl Future` 的函式,而回傳值是會立即計算 (evaluate) 的 closure: 162 | 163 | ```rust 164 | // only arg1's lifetime is captured in the returned future 165 | fn foo<'a>(arg1: &'a str, arg2: &str) -> impl Future + 'a { 166 | // do some initialization using arg2 167 | 168 | // closure which is evaluated immediately 169 | async move { 170 | // asynchronous portion of the function 171 | } 172 | } 173 | ``` 174 | 175 | ## await 展開後的程式碼 176 | 177 | 內嵌的 `await!` 展開結果大致如下: 178 | 179 | ```rust 180 | let mut future = IntoFuture::into_future($expression); 181 | let mut pin = unsafe { Pin::new_unchecked(&mut future) }; 182 | loop { 183 | match Future::poll(Pin::borrow(&mut pin), &mut ctx) { 184 | Poll::Ready(item) => break item, 185 | Poll::Pending => yield, 186 | } 187 | } 188 | ``` 189 | 190 | 這不是真正意義上的『展開』,因為 `yield` 概念不能用 `async` 函式中的表層語法來表達。這就是為什麼 `await!` 是一個內建編譯器函式,而不是實際的巨集。 191 | 192 | ## `async` 和 `move` 的順序 193 | 194 | 非同步 closure 和區塊可以用 `move` 註釋來捕捉它們包覆的變數的所有權。關鍵字的順序固定為 `async move`。只允許一種順序,可以避免語義上「是否有重要意義」的混淆。 195 | 196 | ```rust 197 | async move { 198 | // body 199 | } 200 | ``` 201 | 202 | # 缺點 203 | [缺點]: #drawbacks 204 | 205 | 在 Rust 中新增 async 和 await 語法是對語言的重大更改 - 這是自 1.0 以來最重要的新增功能之一。雖然我們從最小的功能開始,但從長遠來看,它所隱含的功能集也會增長(請參閱未解決的問題部分)。對於這樣一個重要的新增功能,我們絕不能掉以輕心,只有在強烈的動機下才能進行。 206 | 207 | 我們相信,一個符合人因工程學的非同步 IO 解決方案對於 Rust 作為撰寫高效能網路服務的語言的成功至關重要,這是我們 2018 年的目標之一。基於 Future trait 的 async & await 語法是在不久的將來實現這一目標最便捷和低風險的途徑。 208 | 209 | 這個 RFC,連同其配套的 lib RFC,對 future 和 async/await 做出比我們過往的專案更堅定的承諾。如果我們在穩定這些特性之後決定反其道而行,那將付出相當大的代價。因為這個 RFC 的存在,增加非同步程式的替代機制的成本會更高。然而,有鑑於我們在 future 方面的經驗,我們相信這是正確的發展方向。 210 | 211 | 我們所做的幾個小決定也有缺點。例如,在使用「内部」回傳型別和「外部」回傳型別之間有一個權衡。我們可以為非同步函式建立一個不同的求值模型,即在第一個等待(await point)點之前立即對其進行求值。我們在這些問題上做出的决定在 RFC 的相應部分都有說明。 212 | 213 | # 原理和替代方案 214 | [alternatives]: #alternatives 215 | 216 | 本節包含了本 RFC 拒絕的替代性設計決定(相對於那些只是推遲的設計)。 217 | 218 | ## 回傳型別(使用 `T` 而不是 `impl Future`) 219 | 220 | 非同步函式的回傳型別是一個有點複雜的問題。對於非同步函式的回傳型別,有兩個不同的觀點:「内部」回傳型別 - 你用 `return` 關鍵字回傳的型別,以及「外部」回傳型別 - 當你呼叫函式時回傳的型別。 221 | 222 | 大多數帶有非同步函式的靜態型別語言在函式簽名中顯示「外部」回傳型別。本 RFC 建議在函式簽名中顯示「内部」回傳型別。這既有優點也有缺點。 223 | 224 | ### 生命週期消除問題 225 | 226 | 正如前面所提到的,回傳的 future 捕捉了所有傳入的生命週期。預設情況下,`impl Trait` 不捕捉任何生命週期。為了準確反應外部回傳型別,有必要消除生命週期的省略: 227 | 228 | ```rust 229 | async fn foo<'ret, 'a: 'ret, 'b: 'ret>(x: &'a i32, y: &'b i32) -> impl Future + 'ret { 230 | *x + *y 231 | } 232 | ``` 233 | 234 | 這將是非常不符合人因工程學的,並且使非同步的使用變得更不愉快,更不易於學習。這個問題在決定回傳內部型別時佔很大比重。 235 | 236 | 我們可以讓它回傳 `impl Future`,但對於 `async fn` 的回傳型別,生命週期捕捉的運作方式與其他函式不同,這似乎比顯示內部型別更糟糕。 237 | 238 | ### 多型的回傳(對我們來說並非是一個因素) 239 | 240 | 根據 C# 開發者的說法,回傳 `Task`(他們的「外部型別」)的主要因素之一是,他們希望有可以回傳 `Task` 以外型別的非同步函式。我們對此沒有一個可以令人信服的實例。 241 | 242 | 1. 在 future 的 0.2 分支中,`Future` 和 `StableFuture` 之間是有區別的。然而,這種區分是人為的,單純是因為物件安全(object-safe)的自定義自型別(self-types)在穩定版本上還不能使用。 243 | 2. 目前的 `#[async]` 巨集有一個 `(boxed)` 變體。我們更傾向於讓非同步函式盡可能的不包裝,只在呼叫處明確包裝。屬性變體的動機是為了支援物件安全特徵中的非同步方法。這是在物件安全特徵中支援 `impl Trait` 的一個特例(可能是透過在物件情况下對回傳型別進行包裝),我們希望這個特性與非同步函式分開。 244 | 3. 有人建議我們支援回傳串流(stream)的 `async fn`。然而,這意味著内部函式的語意在回傳 future 和串流的函式之間會有顯著的不同。正如在未解决的問題部分所討論的,基於生成器和非同步生成器的解決方案似乎更有機會。 245 | 246 | 基於這些原因,我們認為從多型的角度來看,回傳外部型別的論點並不強烈。 247 | 248 | ### 可學習性/文件的權衡 249 | 250 | 從可學習性的角度來看,支援外部和内部回傳型別的論點都有。支援外部回傳型別最有說服力的論據之一是文件:當你閱讀自動產生的 API 文件時,你肯定會看到你作為呼叫者得到的東西。相較之下,由於回傳型別和你 `return` 的表達式的型別之間的對應關係,可以更容易理解如何使用内部回傳型別撰寫非同步函式。 251 | 252 | Rustdoc 可以透過幾種方式處理使用内部回傳型別的非同步函式,使其更容易理解。我們至少應該確保在文件中包含 `async` 註解,這樣了解 async 符號的使用者就知道此函式將回傳一個 future。我們還可以進行其他轉換,可能是可選的,以顯示函式的外部簽名。如何確切處理非同步函式的 API 文件是一個尚未解决的問題。 253 | 254 | ## 內嵌語法,而不是在生成器中使用巨集 255 | 256 | 另一個選擇是專注於穩定程序性巨集(procedural macro)和生成器,而不是為非同步函式引入內嵌語法。一個非同步函式可以被建模為一個生成器,它將產生 `()`。 257 | 258 | 從長遠來看,我們相信我們會希望有專門的語法來處理非同步函式,因為它更符合人因工程學原理,而且使用情境也足夠令人信服和重要,可以證明這一點(類似於 - 例如 - 有内嵌的 for 迴圈和 if 判斷式,而不是有編譯成迴圈和配對判斷式的巨集)。鑑於此,唯一的問題是,我們是否可以透過暫時使用生成器來獲得比現在引入非同步函式更好的穩定性。 259 | 260 | 使用展開到生成器的巨集似乎不太可能使其更快的穩定。生成器可以表現更多的可能性,並且有更多的開放性問題 - 包括語法和語意。這甚至沒有解決穩定更多程序性巨集的開放問題。出於這個原因,我們認為穩定最小的内嵌 async/await 功能比試圖穩定生成器和 proc 巨集更有效益。 261 | 262 | ## 單純基於生成器的 `async` 263 | 264 | 另一種設計是將非同步函式作為建立生成器的語法。在這種設計中,我們可以寫一個這樣的生成器: 265 | 266 | ```rust 267 | async fn foo(arg: Arg) -> Return yield Yield 268 | ``` 269 | 270 | return 和 yield 都是可選的,預設為 `()`。一個產生 `()` 的非同步函式將使用全面實作(blanket impl)來實作 `Future`。一個回傳 `()` 的非同步函式將實作 `Iterator`。 271 | 272 | 此方法的問題是,它不能從人因工程學的角度處理 `Stream`,Stream 需要產生 `Poll>`。目前還不清楚在一個產生 `()` 以外的東西(包括 Stream)的非同步函式裡的 `await` 如何運作。由於這個原因,「矩陣」方法,即我們對生成器函式、非同步函式和非同步生成器函式有獨立的語法,似乎是一個更可行的方法。 273 | 274 | ## "Hot async functions" 275 | 276 | 正如本 RFC 所建議的,所有的非同步函式都會立即回傳,根本不需要執行其主體。如上所述,這對於需要立即進行「初始化」步驟的情境來說並不方便 - 例如,這些情境需要使用一個終端非同步區塊。 277 | 278 | 另一種方法是讓非同步函式立即求值,直到它們的第一個 `await`,在那之前保留它們的狀態。這將是一個相當複雜的實現 - 它們需要在 `await` 中擁有一个額外的 yield point,在輪詢被 await 的 future 之前,條件是 await 是否是 future 主體中的第一個 await。 279 | 280 | Rust 的 future 與其它語言的 future 的一個根本區別是,Rust 的 future 除非被輪詢,否則不會做任何事情。整個系统都是圍繞這一點建立的:例如,取消正是因為這個原因而捨棄了 future。相反,在其它語言中,呼叫一個非同步函式會產生一個立即開始執行的 future。這種差異也延續到了 `async fn` 和 `async` 區塊中,其中至關重要的是,產生的 future 要**主動輪詢**以取得進展。允許部分、急迫的執行很可能會引發嚴重的混亂和錯誤。 281 | 282 | 從使用者的角度來看,這也很複雜 - 主體的一部分何時被執行取決於它是否出現在所有 `await` 語句(可能是巨集生成的)之前。使用终端 async 區塊提供了一個更清晰的機制來區分帶有初始化步驟的 future 中立即執行部分和非同步執行部分。 283 | 284 | ## 使用 async/await 而不是其他的非同步性系統 285 | 286 | 最後,一個極端的選擇是放棄 future 和 async/await 作為 Rust 中 async/await 的機制,而採用不同的典範。在這些建議中,有一個常見的效果系统,monad 和 do 語法(do notation)、綠色執行緒和滿堆疊(stack-full)的協程。 287 | 288 | 假設性上來說,Rust 可以透過 async/await 語法來達成一些泛化(generalization),但在這個領域的研究還不足以在短期内支援它。考慮到我們 2018 年的目標 - 強調 - async/await 語法(一個在許多語言中廣泛存在的概念,與我們現有的 async IO 函式庫運作良好)是在 Rust 發展的這個階段最合理的實作。 289 | 290 | ## 非同步區塊與非同步 closure 291 | 292 | 正如文中所指出的,非同步區塊和非同步 closure 是密切相關的,而且大致上是可以相互表達的: 293 | 294 | ```rust 295 | // almost equivalent 296 | async { ... } 297 | (async || { ... })() 298 | 299 | // almost equivalent 300 | async |..| { ... } 301 | |..| async { ... } 302 | ``` 303 | 304 | 我們可以考慮只採用兩個結構中的其中一個。然而: 305 | 306 | - 為了與 `async fn` 保持一致,我們有充分的理由使用 `async ||`;這樣的 closure 通常對建構一個服務這樣的高階構造很有用。 307 | 308 | - 有一個強而有力的理由讓我們採用非同步區塊。RFC 文件中提到的初始化模式,以及事實上它提供了一種更直接、更原始的方式建立 future。 309 | 310 | RFC 提議在前面就包含這兩個構造,因為我們似乎不可避免地需要這兩者,但我們總是可以在穩定之前重新思考這個問題。 311 | 312 | # 現有技術 313 | [現有技術]: #prior-art 314 | 315 | 在其他語言中,包含 C#、JavaScript 和 Python,有很多關於使用 async/await 語法作為處理非同步操作的一種方式的先例。 316 | 317 | 目前主流的非同步程式設計有以下三種範式: 318 | 319 | - async 和 await 符號。 320 | - 隱式並行的執行階段程式庫(implicit concurrent runtime),通常稱為「綠色執行緒」,例如通信順序行程(例如 Go)或參與者模型(例如 Erlang)。 321 | - 延遲執行程式中的 Monadic 轉換,例如:Haskell 的 do 語法(do notation)。 322 | 323 | async/await 是 Rust 最引人注目的模型,因為它與所有權和借用互動良好(不像基於 Monadic 的系统),而且它使我們能夠擁有一個完全基於函式庫的非同步模型(不像綠色執行緒)。 324 | 325 | 我們對 async/await 的處理不同於大多數其他靜態型別語言(例如 C#),我們選擇顯示「內部」回傳型別,而不是外部回傳型別。正如在替代方案部分中討論的那樣,在 Rust 定義的特定脈絡下(生命週期省略,不需要回傳型別多型),這種偏差的動機充分。 326 | 327 | # 未解決的問題 328 | [未解決的問題]: #unresolved-questions 329 | 330 | 本節包含已推延且未包含在此初始 RFC 中設計的延伸。 331 | 332 | ## `await` 表達式的最終語法 333 | 334 | 儘管此 RFC 建議 `await` 是一個內建的巨集,但我們希望有一天它成為一個正常的控制流結構。如何處理它的運算子優先順序,以及是否需要某種分隔符號則尚待解決。 335 | 336 | 特別是, `await` 與 `?` 有一個有趣的互動。很常見的情況是有一個 future,它將被執行為一個 `Result`,然後使用者會想把這個结果應用到 `?` 這意味著 await 應該比 `?` 有更高的優先順序,這樣該模式就能按照使用者的意願運作。然而,由於它引入了一個空格,看起來這並不是你要得到的優先順序: 337 | 338 | ``` 339 | await future? 340 | ``` 341 | 342 | 以下有幾種可能的解決方案: 343 | 344 | 1. 需要某種的分隔符號,可能是大括號或括號或兩者之一,讓它看起來更符合期望的那樣 - await {future}? - 這很煩躁。 345 | 2. 將優先順序定義為,如果優先順序不符使用者本意,需要使用者明確指出 `(await future)?` - 對使用者來說非常令人驚訝。 346 | 3. 將其定義為不方便的優先順序 —— 這似乎與其他優先順序一樣令人驚訝。 347 | 4. 引入一種特殊的語法來處理多個應用程式,例如 `await? future` - 這似乎是很不尋常的方式。 348 | 349 | 這個問題留給未來找尋另一種解決方案,或是從上述方案中選擇最不糟糕的一個。 350 | 351 | ## `for await` 和處理串流 352 | 353 | RFC 目前遺漏的另一個延伸是使用 for 迴圈處理串流的能力。可以想像 `for await` 這樣的結構,它採用 `IntoStream` 而不是 `IntoIterator`: 354 | 355 | ```rust 356 | for await value in stream { 357 | println!("{}", value); 358 | } 359 | ``` 360 | 361 | 這被排除在最初的 RFC 之外,以避免必須在標準庫中穩定 `Stream` 的定義(以使與此相關的 RFC 盡可能小)。 362 | 363 | ## 生成器和串流 364 | 365 | 將來,我們可能還希望能夠定義對串流求值非同步函式,而不是對 future 求值。我們建議透過生成器來處理這個案例。生成器可以轉換為一種迭代器,而非同步生成器可以轉換為一種串流。 366 | 367 | 例如(使用的語法可能會改變); 368 | 369 | ```rust 370 | // Returns an iterator of i32 371 | fn foo(mut x: i32) yield i32 { 372 | while x > 0 { 373 | yield x; 374 | x -= 2; 375 | } 376 | } 377 | 378 | // Returns a stream of i32 379 | async fn foo(io: &AsyncRead) yield i32 { 380 | async for line in io.lines() { 381 | yield line.unwrap().parse().unwrap(); 382 | } 383 | } 384 | ``` 385 | 386 | ## 實現 `Unpin` 的非同步函式 387 | 388 | 如本 RFC 中所提議,所有非同步函式均未實現 `Unpin`,因此將它們從 `Pin` 中移出是不安全的。這允許它們包含跨越 yield point 的引用。 389 | 390 | 我們還可以透過註釋對非同步函式進行型別檢查,以確認它不包含任何跨越 yield point 的引用,從而允許它實做 `Unpin`。可啟用此功能的註釋暫時未指定。 391 | ## 異步區塊中的 `?` 運算子和控制流構造 392 | 393 | 這個 RFC 沒有提出 `?` 運算子和控制流結構如 `return`、`break` 和 `continue` 應該如何在非同步區塊中工作。 394 | 395 | 不過有討論非同步區塊應該充當 `?` 運算子的邊界。讓它們適用於易出錯的 IO: 396 | 397 | ```rust 398 | let reader: AsyncRead = ...; 399 | async { 400 | let foo = await!(reader.read_to_end())?; 401 | Ok(foo.parse().unwrap_or(0)) 402 | }: impl Future> 403 | ``` 404 | 405 | 此外,還討論了允許使用 break 從非同步區塊中提前回傳: 406 | 407 | ```rust 408 | async { 409 | if true { break "foo" } 410 | } 411 | ``` 412 | 413 | 使用 `break` 關鍵字而不是 `return` 可能有助於表明它適用於非同步區塊而不是其周圍的函式。另一方面,這會給使用 `return` 關鍵字的 closure 和非同步 closure 帶來區別。 414 | -------------------------------------------------------------------------------- /text/2495-min-rust-version.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `min_rust_version` 2 | - Start Date: 2018-06-28 3 | - RFC PR: [rust-lang/rfcs#2495](https://github.com/rust-lang/rfcs/pull/2495) 4 | - Rust Issue: [rust-lang/rust#65262](https://github.com/rust-lang/rust/issues/65262) 5 | - Translators: [[@weihanglo](https://github.com/weihanglo)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/3071138d4ed510d6dfc1f8e1d7e9d4b099ea12e8/text/2495-min-rust-version.md) 7 | - Updated: 2020-09-07 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 在 `Cargo.toml` 的 package 區塊加入 `rust` 欄位,用於指定 crate 的最低支援 Rust 版本(Minimum Supported Rust Version,MSRV)。 13 | 14 | ```toml 15 | [package] 16 | name = "foo" 17 | version = "0.1.0" 18 | rust = "1.30" 19 | ``` 20 | 21 | # 動機 22 | [動機]: #動機 23 | 24 | 當前 crate 無任何正式方法指定 MSRV,導致使用者無法在不建構該 crate 的情況下得知 crate 是否可透過他們的工具鏈(toolchain)建構。這也帶來有關如何在提升 MSRV 時管理 crate 版本的爭論,保守的作法是將之視為破壞性改動,這種作法會阻礙整個生態採納新功能,或使得版本號通膨式提升,進而讓下游的 crate 很難跟上。另一方面,若作法稍微寬鬆些,則導致採用較舊編譯器版本的使用者無法成功編譯它們的 crate。 25 | 26 | # 教學式解說 27 | [教學式解說]: #教學式解說 28 | 29 | 若你想指定一個特定 MSRV 版本,請在 `Cargo.toml` 的 `[package]` 區塊中的 `rust` 欄位設定指定的 Rust 版本。如果你建構一個 crate 時有任一依賴要求的 MSRV 比你當前工具鏈更高,會導致編譯錯誤,指明該依賴與它的 MSRV。這個行為可以透過 `--no-msrv-check` 選項來停用。 30 | 31 | # 技術文件式解說 32 | [技術文件式解說]: #技術文件式解說 33 | 34 | 在建構過程(包含 `run`、`test`、`benchmark`、`verify` 與 `publish` 子指令),`cargo` 會以依賴樹(dependency tree)的形式,檢查所有將要建構或檢查的所有 crate 之 MSRV 要求。在依賴樹內但不會被構建的 crate 不會執行此項檢查(例如特定目標平台或可選的 crate)。 35 | 36 | `rust` 欄位應至少遵循下列要求: 37 | 38 | - 其值需遵守語意化版號且不能有範圍運算子。注意,「1.50」是合法的值,代表「1.50.0」。 39 | - 版本不能比當前 stable 工具鏈高(上傳 crate 時 crates.io 會檢查)。 40 | - 版本不能低於 1.27(此版本將 `package.rust` 欄位從錯誤改為警告)。 41 | - 版本不能比選用的 edition 釋出的版本低,舉例來說,同時出現 `rust = "1.27"` 與 `edition = 2018` 是非法的。 42 | 43 | # 未來展望與延伸 44 | [未來展望與延伸]: #未來展望與延伸 45 | 46 | ## 對版本解析之影響 47 | 48 | `rust` 欄位之值(手動設定或 `cargo` 自動選擇)會用於選擇合適的依賴版本。 49 | 50 | 舉例來說,想像你的 crate 相依 crate `foo`,一個發佈了從 `0.1.0` 到 `0.1.9` 十個版本的 crate,其中 `0.1.0` 到 `0.1.5` 版在 crates.io 上 `Cargo.toml` 的 `rust` 欄位為「1.30」,其他版本則為「1.40」。現在,你建構一個專案用了例如 Rust 1.33 版,`cargo` 會選用 `foo v0.1.5`。`foo v0.1.9` 只會在你用 Rust 1.40 或更高版本建構專案時選用。倘若你嘗試使用 Rust 1.29 建構專案,cargo 會回報錯誤。 51 | 52 | `rust` 欄位值也會被檢核。在 crate 建構過程,`cargo` 將檢查所有上游相依是否可以在指定 MSRV 下建構。(例如,檢查給定的 crate 與 Rust 版本限制條件下是否存在解)已除去(yank)的 crate 會略過這個檢查步驟。 53 | 54 | 期望實作這項功能得以替長久以來對 MSRV 提升是否為破壞性改動的爭論劃下休止符,並讓 crate 作者提升 crate 的 MSRV 不再如此綁手綁腳。(儘管對於已 1.0 的 crate 來說,透過提升修訂號(patch version)來提升 MSRV 次版號,以接納修復嚴重問題的 backport,可能是個有用的慣例) 55 | 56 | 注意,上述 MSRV 限制與依賴版本解析檢查,可以透過 `--no-msrv-check` 選項停用。 57 | 58 | ## 發佈時檢查 MSRV 59 | 60 | `cargo publish` 將檢查上傳是以 `rust` 欄位指定的工具鏈版本完成,若工具鏈版本有異,`cargo` 會拒絕上傳該 crate。此確保機制避免因為未預期的 MSRV 提升導致錯誤的 `rust` 欄位值。這項檢查可透過既有的 `--no-verify` 選項停用。 61 | 62 | ## 將 `rust` 欄位設為必填 63 | 64 | 未來(可能是下一個 edition),我們可以設定新上傳的 crate 的 `rust` 欄位為必填。既有 crate 的 MSRV 會透過 `edition` 決定。換句話說 `edition = 2018` 之 MSRV 必然是 `rust = "1.31"`,而 `edition = "2015"` 則是 `rust = "1.0"`。 65 | 66 | `cargo init` 將會使用當前工具鏈使用的版本。 67 | 68 | ## 基於 `cfg` 的 MSRV 69 | 70 | 部分 crate 會根據不同目標架構平台或啟用的功能而有不同的 MSRV。可透過以下方式有效指定 MSRV 如何依賴這些配置: 71 | 72 | ```toml 73 | [package] 74 | rust = "1.30" 75 | 76 | [target.x86_64-pc-windows-gnu.package] 77 | rust = "1.35" 78 | 79 | [target.'cfg(feature = "foo")'.package] 80 | rust = "1.33" 81 | ``` 82 | 83 | 在 `target` 區塊中所有 `rust` 值應等於或高於在 `package` 區塊的 `rust` 值。 84 | 85 | 若 `target` 的條件為真,`cargo` 會取用該區塊的 `rust` 值。若多個 target 區塊的條件為真,則取用最大值。 86 | 87 | ## Nightly 與 stable 版本 88 | 89 | 部分 crate 可能偏好在最新 stable 或 nighly 工具鏈,除了指定版本之外,我們可允許宣告 `stable` 或 `nightly` 值,讓維護者不需追蹤該 crate 的 MSRV 。 90 | 91 | 對於某些超前沿的 crate(例如: `rocket`)常常因為 Nightly 更新就壞,將可指定特定可成功建構的 Nightly 版本。透過下列語法來達成: 92 | 93 | - 自動選擇:`nightly` 此寫法與寫 `stable` 的行為一致,將使用等於當前或更高的 nightly 版本。 94 | - 單一版本:`nightly: 2018-01-01` (主要寫法) 95 | - 列舉:`nightly: 2018-01-01, 2018-01-15` 96 | - 類語意化版本條件:`nightly: >=2018-01-01`、`nightly: >=2018-01-01, <=2018-01-15`、`nightly: >=2018-01-01, <=2018-01-15, 2018-01-20`。(後者會解讀為 「(version >= 2018-01-01 && version <= 2018-01-20) || version == 2018-01-20」) 97 | 98 | 這些條件或許很嚴苛,盼使用這功能的 crate 一隻手數得出來。 99 | 100 | # 缺點 101 | [缺點]: #缺點 102 | 103 | - 即使宣告了 MSRV 且檢查過,並無法保持 crate 能夠正確在指定 MSRV 下正確執行,只有合理配置的 CI 能做到此事。 104 | - 更複雜的依賴版本解析演算法。 105 | - 使用 `cargo publish` 配合 MSRV `rust = "stable"` 恐過於保守。 106 | 107 | # 替代方案 108 | [替代方案]: #替代方案 109 | 110 | - 自動計算 MSRV。 111 | - 不做任何事,依靠 [LTS 發行](https://github.com/rust-lang/rfcs/pull/2483) 的 crate MSRV 提升。 112 | - 允許在 [RFC 2523](https://github.com/rust-lang/rfcs/pull/2523) 中提出基於版本與路徑的 `cfg` 屬性(attribute) 113 | 114 | # 先驅技術 115 | [先驅技術]: #先驅技術 116 | 117 | 早先的提案: 118 | 119 | - [RFC 1707](https://github.com/rust-lang/rfcs/pull/1707) 120 | - [RFC 1709](https://github.com/rust-lang/rfcs/pull/1709) 121 | - [RFC 1953](https://github.com/rust-lang/rfcs/pull/1953) 122 | - [RFC 2182](https://github.com/rust-lang/rfcs/pull/2182)(這個非常有爭議的離題了) 123 | 124 | # 未解決問題 125 | [未解決問題]: #未解決問題 126 | 127 | - 瑣碎的命名問題:`rust` 或 `rustc` 還是 `min-rust-version` 128 | - 額外的檢查? 129 | - 更優質地說明版本解析演算法 130 | - nightly 版本如何與「基於 cfg 的 MSRV」運作? 131 | -------------------------------------------------------------------------------- /text/2592-futures.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `futures_api` 2 | - Start Date: 2018-11-09 3 | - RFC PR: [rust-lang/rfcs#2592](https://github.com/rust-lang/rfcs/pull/2592) 4 | - Rust Issue: [rust-lang/rust#59113](https://github.com/rust-lang/rust/issues/59113) 5 | - Translators: [[@FizzyElt](https://github.com/FizzyElt)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/a31e5941133de5935b97b5d3e09b921b6e796abb/text/2592-futures.md) 7 | - Updated 2022-08-13 8 | 9 | # 概要 10 | [概要]: #summary 11 | 12 | 此 RFC 提議替一等公民語法 `async`/`await` 穩定相關函式庫組件。特別是,它將穩定: 13 | 14 | - `std` 中任務系統所有的 API,例如 `std::task::*`。 15 | - 核心 `Future` API,例如 `core::future::Future` 和 `std::future::Future`。 16 | 17 | 它**不提議**穩定任何 `async`/`await` 語法本身,該語法自有另外的提議。它也不包括已在[別處](https://github.com/rust-lang/rust/issues/55766)提出的 `Pin` API 的穩定性。 18 | 19 | 這是早期 [futures RFC](https://github.com/rust-lang/rfcs/pull/2418) 的修訂和精簡版本,該 RFC 已暫緩,直到 nightly 版本獲得更多經驗之後。 20 | 21 | [pin]: https://github.com/rust-lang/rfcs/pull/2349 22 | [companion RFC]: https://github.com/rust-lang/rfcs/pull/2394 23 | 24 | # 動機 25 | [動機]: #motivation 26 | 27 | ## 為何 `Future` 要在 `std` 之中? 28 | 29 | 此 RFC 的核心動機是穩定 `async`/`await` 語法的支援機制。語法本身是在(已經合併的)[配套 RFC](https://github.com/rust-lang/rfcs/pull/2394) 中提出的,並且有一篇[部落格文章](https://aturon.github.io/tech/2018/04/24/async-borrowing/)更詳細介紹它的重要性。 30 | 31 | 與 closures 一樣,`async` 語法涉及到生成一個匿名型別,該型別實作一個關鍵特徵:`Future`。因為 `async`/`await` 需要語言層級的支援,所以底層特徵也必須是標準函式庫的一部分。因此,這個 RFC 的目標是穩定這個 `Future` 特徵和它所依賴的型別。這是我們能夠穩定 `async`/`await` 本身之前所需的最後一步。 32 | 33 | ## 這一步是如何融入更大的藍圖之中? 34 | 35 | 36 | `async`/`await` 語法是 Rust 中最渴求的特性之一,它將對整個生態系產生重大影響。自 2018 年 5 月下旬以來,該語法及此處描述的 API 已可於 nightly 版本使用,並被大量採用。 37 | 38 | 穩定此設計的 futures API 部分使函式庫更容易在穩定的 Rust 上工作並無縫支援在 nightly 版本使用 `async`/`await`。它還允許我們完成關於 API 部分的設計討論,並專注於在 `async` 語法穩定之前剩下的幾個問題。 39 | 40 | # 歷史背景 41 | 42 | 這些被提議要穩定的 API 有著悠久的歷史: 43 | 44 | - `Future` 特徵從 futures crate 開始; [0.1 於 2016 年 8 月發布](https://aturon.github.io/tech/2016/08/11/futures/)。該版本確立了「任務/輪詢」模型的核心思想,以及此處保留的 API 的許多其他方面。 0.1 系列持續在整個 Rust 生態系和正式系統中大量使用。 45 | 46 | - 2018 年初,隨著 `async`/`await` 的工作開始,futures 團隊建立了一個 RFC 流程並編寫了幾個 [RFC](https://github.com/rust-lang-nursery/futures-rfcs/pulls?q=is%3Apr+is%3Aclosed),以根據長期的社群回饋對核心 API 進行修訂。這些 RFC 最終產生了 [0.2le](https://aturon.github.io/tech/2018/02/27/futures-0-2-RC/) 版本,並於 4 月[釋出](https://aturon.github.io/tech/2018/04/06/futures2/)。 47 | 48 | - 在同一時期,@withoutboats 在支援 `async` 區塊內借用的 pinning API 上的工作已經[完成](https://boats.gitlab.io/blog/post/2018-04-06-async-await-final/)。[pinning API](https://github.com/rust-lang/rfcs/pull/2349) 顛覆了過往一切,可在不使 future 的核心 API 成為 unsafe 前提下,支援 borrowing-across-yield。 49 | 50 | - 2018 年 4 月,一對 RFC 正式提出了 `async`/`await` 語法以及 futures API 的進一步修訂(以利用 pinning API 帶來的好處);後者經歷了許多修訂,包括一個新的 [RFC](https://github.com/rust-lang/rfcs/pull/2418)。最終,語法 [RFC](https://github.com/rust-lang/rfcs/pull/2394#issuecomment-387550523) 在 5 月被合併,而 API RFC 被關閉,進一步的設計疊代將在 nightly 版本進行,隨後是一個穩定的 RFC:這個! 51 | 52 | - 這些 API 在 5 月底[加入到 `std`](https://github.com/rust-lang/rust/pull/51263)。 53 | 54 | - 從那時起,隨著我們從 API 獲得的經驗,語法、`std` API 和 futures 0.3 crate 都在同步發展。這種體驗的主要驅動者是 Google 的 Fuchsia 專案,該專案在作業系統設置中大規模使用所有這些功能。 55 | 56 | - 最近的修訂是在 8 月,其中涉及一些關於如何使 `Pin` API 有更清晰的[見解](https://boats.gitlab.io/blog/post/rethinking-pin/)。這些 API 已被[提議要穩定](https://github.com/rust-lang/rust/issues/55766),以及它們作為 [`self` 型別的用途](https://github.com/rust-lang/rust/issues/55786)。 57 | 58 | - 有多個相容層可以同時使用 futures 0.1 和 0.3。這很重要,因為它允許現有生產程式碼的**增量**遷移。 59 | 60 | 自從最初的 futures 0.3 發布以來,除了上面提到的改進之外,核心 `Future` 特徵和任務系統的變化相對較小。實際的 `Future` 特徵基本上與 4 月時相同。 61 | 62 | # 教學式解說 63 | [教學式解說]: #guide-level-explanation 64 | 65 | `Future` 特徵代表了一個非同步和惰性計算,它最終可能會產生一個最終值,但不以阻塞當前執行緒為前提來達成。 66 | 67 | Future 可以通過 `async` 區塊或 `async` 函式來創建,例如 68 | 69 | ```rust 70 | async fn read_frame(socket: &TcpStream) -> Result { ... } 71 | ``` 72 | 73 | 當呼叫這個 `async` 函式,會產生一個 future,代表完成了從给定的 socket 中讀取一個框(frame)。函式簽名等同於: 74 | 75 | ```rust 76 | fn read_frame<'sock>(socket: &'sock TcpStream) 77 | -> impl Future> + 'sock; 78 | ``` 79 | 80 | 其他非同步函數可以 **await** 這個 future; 有關完整詳細信息,請參閱[隨附的 RFC](https://github.com/rust-lang/rfcs/pull/2394)。 81 | 82 | 除了 `async fn` 定義,future 還可以使用轉接器(adapter)來創建,就像 `Iterator` 一樣。最初,這些轉接器將完全由其他 crate 所提供,但最终它們將帶入標準函式庫。 83 | 84 | 最終非同步計算以**任務**的形式執行,相當於輕量級執行緒。 **executor** 提供了從 `()` 生成的 `Future` 創建任務的能力。執行器將固定 `Future` 並對其進行 `poll`,直到為其創建的任務完成。 85 | 86 | executor 的實作以協同式地(cooperative)排程它的任務。是否使用一個或多個作業系統執行緒以及可以在其之上平行生成多少任務取決於 executor 的實作。一些 executor 的實作可能只能驅動單個 `Future` 完成,而另一些則可以提供動態接受新 `Future` 的能力,這些 Future 是在任務中被驅動完成。 87 | 88 | 該 RFC 不包含任何 executor 的定義。它僅以 API 的形式定義了 executors、tasks 和 `Future` 之間的交互關係,這些 API 允許任務請求再次進行排程。 這些 API 由 `task` 模組提供,在手動實作 `Future` 或 executor 時需要這些 API。 89 | 90 | # 技術文件式解說 91 | [技術文件式解說]: #reference-level-explanation 92 | 93 | ## `core::task` 模組 94 | 95 | Rust 中非同步運算的基本機制是**任務**,它們是輕量級的執行緒;許多任務可以協同調度到單個作業系統執行緒上。 96 | 97 | 為了執行這種協作調度,我們使用了一種有時被稱為「[trampoline](https://en.wikipedia.org/wiki/Trampoline_(computing))」的技術。當一個任務需要阻塞等待某個事件時,它會保存一個物件,允許它稍後再次被調度並**返回**到運行它的執行器,然後它可以執行另一個任務。隨後的喚醒將任務放回就緒任務的執行者隊列中,就像作業系統中的執行緒調度程序一樣。 98 | 99 | 嘗試完成一個任務(或其中的非同步值)稱為**輪詢**,並且總是返回一個 `Poll` 值: 100 | 101 | ```rust 102 | /// Indicates whether a value is available, or if the current task has been 103 | /// scheduled for later wake-up instead. 104 | #[derive(Copy, Clone, Debug, PartialEq)] 105 | pub enum Poll { 106 | /// Represents that a value is immediately ready. 107 | Ready(T), 108 | 109 | /// Represents that a value is not ready yet. 110 | /// 111 | /// When a function returns `Pending`, the function *must* also 112 | /// ensure that the current task is scheduled to be awoken when 113 | /// progress can be made. 114 | Pending, 115 | } 116 | ``` 117 | 118 | 當一個任務返回 `Poll::Ready` 時,執行器知道該任務已經完成並且可以被刪除。 119 | 120 | ### 喚醒 121 | 122 | 如果 future 在執行期間無法直接完成並返回 `Pending`,則它需要一種方法來稍後通知執行器它需要再次輪詢以取得進展。 123 | 124 | 此功能是透過一組 `Waker` 型別提供的。 125 | 126 | `Waker` 是作為參數傳遞給 `Future::poll` 調用的物件,並且可以透過這些 `Futures` 的實現來儲存。每當一個 `Future` 需要再次被輪詢時,它可以使用喚醒器的 `wake` 方法來通知執行器擁有 `Future` 的任務應該被再次調度和執行。 127 | 128 | RFC 定義了一個具體的 `Waker` 型別,`Futures` 和非同步函式的實作者將與之互動。此型別定義了一個 `wake(&self)` 方法,用於安排與 `Waker` 關聯的任務再次輪詢。 129 | 130 | 再次調度任務的機制取決於驅動任務的執行器。喚醒執行器的可能方法包含: 131 | - 如果執行器在條件變數上被阻塞,則需要通知條件變數。 132 | - 如果執行器在諸如 `select` 之類的系統調用上被阻塞,它可能需要被諸如 `write` 管道之類的系統調用喚醒。 133 | - 如果執行器的執行緒被放置,喚醒呼叫需要將其取消放置。 134 | 135 | 為了讓執行器實現自定義喚醒行為,`Waker` 型別包含一個稱作 `RawWaker` 的型別,它由指向自定義可喚醒物件的指針和對其提供 `clone`、`wake` 和 `drop` 函式的虛擬函數指針表 (vtable) 的引用組成底層可喚醒物件。 136 | 137 | 選擇這種機制有利於特徵物件,因為它允許更靈活的記憶體管理方案。 `RawWaker` 可以單純根據全域函式和狀態、在引用計數物件之上或以其他方式實現。這種策略還可以更容易地提供不同的 vtable 函式,這些函式將執行不同的行為,儘管引用了相同底層可喚醒的物件型別。 138 | 139 | 這些 `Waker` 型別之間的關係在以下定義中進行了概述: 140 | ```rust 141 | /// A `RawWaker` allows the implementor of a task executor to create a `Waker` 142 | /// which provides customized wakeup behavior. 143 | /// 144 | /// It consists of a data pointer and a virtual function pointer table (vtable) that 145 | /// customizes the behavior of the `RawWaker`. 146 | #[derive(PartialEq)] 147 | pub struct RawWaker { 148 | /// A data pointer, which can be used to store arbitrary data as required 149 | /// by the executor. This could be e.g. a type-erased pointer to an `Arc` 150 | /// that is associated with the task. 151 | /// The value of this field gets passed to all functions that are part of 152 | /// the vtable as first parameter. 153 | pub data: *const (), 154 | /// Virtual function pointer table that customizes the behavior of this waker. 155 | pub vtable: &'static RawWakerVTable, 156 | } 157 | 158 | /// A virtual function pointer table (vtable) that specifies the behavior 159 | /// of a `RawWaker`. 160 | /// 161 | /// The pointer passed to all functions inside the vtable is the `data` pointer 162 | /// from the enclosing `RawWaker` object. 163 | #[derive(PartialEq, Copy, Clone)] 164 | pub struct RawWakerVTable { 165 | /// This function will be called when the `RawWaker` gets cloned, e.g. when 166 | /// the `Waker` in which the `RawWaker` is stored gets cloned. 167 | /// 168 | /// The implementation of this function must retain all resources that are 169 | /// required for this additional instance of a `RawWaker` and associated 170 | /// task. Calling `wake` on the resulting `RawWaker` should result in a wakeup 171 | /// of the same task that would have been awoken by the original `RawWaker`. 172 | pub clone: unsafe fn(*const ()) -> RawWaker, 173 | 174 | /// This function will be called when `wake` is called on the `Waker`. 175 | /// It must wake up the task associated with this `RawWaker`. 176 | pub wake: unsafe fn(*const ()), 177 | 178 | /// This function gets called when a `RawWaker` gets dropped. 179 | /// 180 | /// The implementation of this function must make sure to release any 181 | /// resources that are associated with this instance of a `RawWaker` and 182 | /// associated task. 183 | pub drop_fn: unsafe fn(*const ()), 184 | } 185 | 186 | /// A `Waker` is a handle for waking up a task by notifying its executor that it 187 | /// is ready to be run. 188 | /// 189 | /// This handle encapsulates a `RawWaker` instance, which defines the 190 | /// executor-specific wakeup behavior. 191 | /// 192 | /// Implements `Clone`, `Send`, and `Sync`. 193 | pub struct Waker { 194 | waker: RawWaker, 195 | } 196 | 197 | impl Waker { 198 | /// Wake up the task associated with this `Waker`. 199 | pub fn wake(&self) { 200 | // The actual wakeup call is delegated through a virtual function call 201 | // to the implementation which is defined by the executor. 202 | unsafe { (self.waker.vtable.wake)(self.waker.data) } 203 | } 204 | 205 | /// Returns whether or not this `Waker` and other `Waker` have awaken the same task. 206 | /// 207 | /// This function works on a best-effort basis, and may return false even 208 | /// when the `Waker`s would awaken the same task. However, if this function 209 | /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. 210 | /// 211 | /// This function is primarily used for optimization purposes. 212 | pub fn will_wake(&self, other: &Waker) -> bool { 213 | self.waker == other.waker 214 | } 215 | 216 | /// Creates a new `Waker` from `RawWaker`. 217 | /// 218 | /// The method cannot check whether `RawWaker` fulfills the required API 219 | /// contract to make it usable for `Waker` and is therefore unsafe. 220 | pub unsafe fn new_unchecked(waker: RawWaker) -> Waker { 221 | Waker { 222 | waker: waker, 223 | } 224 | } 225 | } 226 | 227 | impl Clone for Waker { 228 | fn clone(&self) -> Self { 229 | Waker { 230 | waker: unsafe { (self.waker.vtable.clone)(self.waker.data) }, 231 | } 232 | } 233 | } 234 | 235 | impl Drop for Waker { 236 | fn drop(&mut self) { 237 | unsafe { (self.waker.vtable.drop_fn)(self.waker.data) } 238 | } 239 | } 240 | ``` 241 | 242 | `Waker` 必須滿足以下要求: 243 | - 它們必須是可以克隆的。 244 | - 如果 `Waker` 的所有實體都已被刪除,並且它們的關聯任務已被驅動完成,則必須釋放為該任務分配的所有資源。 245 | - 即使關聯的任務已經被驅動完成,在 `Waker` 上調用 `wake()` 也必須是安全的。 246 | - `Waker::wake()` 必須喚醒執行器,即使它是從任意執行緒調用的。 247 | 248 | 因此,實例化 `RawWaker` 的執行程器必須確保滿足這些要求。 249 | 250 | ## `core::future` 模組 251 | 252 | 有了上述所有任務的基礎設施,定義 `Future` 就很簡單了: 253 | 254 | ```rust 255 | pub trait Future { 256 | /// The type of value produced on completion. 257 | type Output; 258 | 259 | /// Attempt to resolve the future to a final value, registering 260 | /// the current task for wakeup if the value is not yet available. 261 | /// 262 | /// # Return value 263 | /// 264 | /// This function returns: 265 | /// 266 | /// - [`Poll::Pending`] if the future is not ready yet 267 | /// - [`Poll::Ready(val)`] with the result `val` of this future if it 268 | /// finished successfully. 269 | /// 270 | /// Once a future has finished, clients should not `poll` it again. 271 | /// 272 | /// When a future is not ready yet, `poll` returns `Poll::Pending` and 273 | /// stores a clone of the [`Waker`] to be woken once the future can 274 | /// make progress. For example, a future waiting for a socket to become 275 | /// readable would call `.clone()` on the [`Waker`] and store it. 276 | /// When a signal arrives elsewhere indicating that the socket is readable, 277 | /// `[Waker::wake]` is called and the socket future's task is awoken. 278 | /// Once a task has been woken up, it should attempt to `poll` the future 279 | /// again, which may or may not produce a final value. 280 | /// 281 | /// Note that on multiple calls to `poll`, only the most recent 282 | /// [`Waker`] passed to `poll` should be scheduled to receive a 283 | /// wakeup. 284 | /// 285 | /// # Runtime characteristics 286 | /// 287 | /// Futures alone are *inert*; they must be *actively* `poll`ed to make 288 | /// progress, meaning that each time the current task is woken up, it should 289 | /// actively re-`poll` pending futures that it still has an interest in. 290 | /// 291 | /// The `poll` function is not called repeatedly in a tight loop-- instead, 292 | /// it should only be called when the future indicates that it is ready to 293 | /// make progress (by calling `wake()`). If you're familiar with the 294 | /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures 295 | /// typically do *not* suffer the same problems of "all wakeups must poll 296 | /// all events"; they are more like `epoll(4)`. 297 | /// 298 | /// An implementation of `poll` should strive to return quickly, and must 299 | /// *never* block. Returning quickly prevents unnecessarily clogging up 300 | /// threads or event loops. If it is known ahead of time that a call to 301 | /// `poll` may end up taking awhile, the work should be offloaded to a 302 | /// thread pool (or something similar) to ensure that `poll` can return 303 | /// quickly. 304 | /// 305 | /// # Panics 306 | /// 307 | /// Once a future has completed (returned `Ready` from `poll`), 308 | /// then any future calls to `poll` may panic, block forever, or otherwise 309 | /// cause bad behavior. The `Future` trait itself provides no guarantees 310 | /// about the behavior of `poll` after a future has completed. 311 | /// 312 | /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending 313 | /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready 314 | /// [`Waker`]: ../task/struct.Waker.html 315 | /// [`Waker::wake`]: ../task/struct.Waker.html#method.wake 316 | fn poll(self: Pin<&mut Self>, waker: &Waker) -> Poll; 317 | } 318 | ``` 319 | 320 | 這裡的大部分解釋都遵循我們已經說過的關於任務系統的內容。一個轉折是 `Pin` 的使用,這使得可以在不同的 `poll` 調用中保留借用資訊(即「borrowing over yield 321 | points」)。固定機制在介紹它的 [RFC](https://github.com/rust-lang/rfcs/pull/2349) 和有關最新修訂的[部落格文章](https://boats.gitlab.io/blog/post/rethinking-pin/)中進行了解釋。 322 | 323 | ## 與 futures 0.1 的關係 324 | 325 | 上面歷史背景部分概述的各種討論涵蓋了從 futures 0.1 到這些 API 的演進。但是,簡而言之,有三個主要轉變: 326 | 327 | - 使用 `Pin<&mut self>` 而不單純是 `&mut self`,這是支援借用 `async` 區塊所必需的。 `Unpin` 標記特徵可用於在手動撰寫 futures 時恢復類似於 futures 0.1 的人因工程和安全性。 328 | 329 | - 從 `Future` 中刪除**內置**錯誤,以支援 `Future` 在可能失敗時返回 `Result`。 futures 0.3 crate 提供了一個 `TryFuture` 特徵,該特徵在 `Result` 中處理,以便在使用 `Result`-producer futures 時提供更好的人因工程。刪除錯誤型別已在之前的執行緒中討論過,但最重要的理由是為 `async fn` 提供一個正交的、組合性的語義,以反映正常的 `fn`,而不是採用特定的錯誤處理風格。 330 | 331 | - 顯式傳遞 `Waker`,而不是將其儲存在執行緒本地存儲中。自 futures 0.1 發布以來,這一直是一個備受爭議的問題,該 RFC 並不打算重新討論這個問題,但總而言之,主要優點是 (1) 在使用手動 futures(與 `async` 區塊相反)時,它更容易分辨哪裡需要環境任務,並且 (2) `no_std` 相容性明顯更好。 332 | 333 | 為了彌補 futures 0.1 和 0.3 之間的差距,有幾個相容性鋪墊,包括一個內置於 futures crate 本身的,您可以簡單地透過使用 `.compat()` 組合器在兩者之間切換。這些相容層使現有生態系可以順利地使用新的 futures API,並使大型程式的漸進式轉換成為可能。 334 | 335 | # 原理、缺點和替代方案 336 | 337 | 該 RFC 是自 1.0 以來對 `std` 提出的最重要的補充之一。它讓我們在標準函式庫中包含一個特定的任務和輪詢模型,並將其與 `Pin` 聯繫起來。 338 | 339 | 到目前為止,我們已經能夠將「任務/輪詢」模型推向幾乎所有 Rust 希望佔據的利基點,而主要的缺點是缺乏 async/await 語法(以及它[支援的借用](https://aturon.github.io/tech/2018/04/24/async-borrowing/))。 340 | 341 | 該 RFC 並未嘗試提供對源自 futures crate 任務模型的完整介紹。可以在以下兩篇部落格文章中找到有關設計原理和替代方案的更完整說明: 342 | 343 | - [Zero-cost futures in Rust](https://aturon.github.io/tech/2016/08/11/futures/) 344 | - [Designing futures for Rust](https://aturon.github.io/tech/2016/09/07/futures-design/) 345 | 346 | 總而言之,futures 的主要替代模型是 callback-base 的方法,在發現當前方法之前嘗試了 callback-base 幾個月。根據我們的經驗,callback 方法在 Rust 中有幾個缺點: 347 | 348 | - 它幾乎在所有地方都強制分配(allocation),因此與 no_std 不相容。 349 | - 它使取消變得**非常**困難,而對於提議的模型,刪除只是「drop」。 350 | - 主觀上,組合器程式碼非常繁瑣,而基於任務的模型則可以輕鬆且快速達成。 351 | 352 | 附帶的 [RFC](https://github.com/rust-lang/rfcs/pull/2394) 中提供了整個 async/await 專案的一些其他先備知識和基本原理。 353 | 354 | 在本節的其餘部分,我們將深入探討特定的 API 設計問題,其中該 RFC 與 futures 0.2 不同。 355 | 356 | ## 移除內嵌錯誤的基本原理、缺點和替代方法 357 | 358 | 在主要特徵中移除內嵌(built-in)錯誤型別有多種原因: 359 | 360 | - **改進的型別檢查和推斷**。錯誤型別是當今使用 futures 組合器時最大的痛點之一,無論是試圖讓不同的型別配對,還是在一段程式碼無法產生錯誤時導致推理失敗。更明確地說,當 `async` 語法可用時,這些問題將不再那麼明顯。 361 | 362 | - **非同步函式**。如果我們保留一個內嵌的錯誤型別,那麼 `async fn` 應該如何工作就不太清楚了:它是否應該總是要求回傳型別是一個 `Result`?如果不是,當回傳非 `Result` 型別時會發生什麼? 363 | 364 | - **組合器清晰度**。按照組合器依賴錯誤與否來將其拆分,可闡明其語義。尤其對於 stream 來說更是如此,其中錯誤處理是常見的混淆來源。 365 | 366 | - **[正交性(orthogonality)](https://en.wikipedia.org/wiki/Orthogonality_(programming))**。一般來說,產生和處理錯誤與核心輪詢機制是分開的,所以在同等條件的情況下,遵循 Rust 的一般設計原則,藉由**組合** `Result` 來處理錯誤似乎更好。 367 | 368 | 綜上所述,即使使用 `TryFuture`,對大量涉及錯誤處理的程式碼來說,仍有真正的缺點: 369 | 370 | - 需要額外的引入(如果程式碼引入 future prelude,則可以避免,我們也許可以更直接地鼓勵這麼做)。 371 | 372 | - 從程式碼角度來說,受一個特徵的**約束**卻**實作**另一個特徵可能會令人困惑。 373 | 374 | 該 RFC 的錯誤處理部分與其他部分是分開的,因此主要的替代方案是保留內嵌的錯誤型別。 375 | 376 | ## 核心特徵設計的基本原理、缺點和替代方案 (wrt `Pin`) 377 | 378 | 撇開上面討論過的錯誤處理之正交性不談,這個 RFC 中的另一主要項目是核心輪詢方法向 `Pin` 轉移,以及它與 `Unpin`/手動撰寫的 futures 之間的關係。在 RFC 討論過程中,我們基本上確定了解決這個問題的三種主要方法: 379 | 380 | - **一個核心特徵**。這就是主要 RFC 文本中採用的方法:只有一個核心 `Future` 特徵,它適用於 `Pin<&mut Self>`。另外還有一個 `poll_unpin` 輔助器,用於在手動實現中使用 `Unpin` futures。 381 | 382 | - **兩個核心特徵**。我們可以提供兩個特徵,例如 `MoveFuture` 和 `Future`,其中一個在 `&mut self` 上運行,另一個在 `Pin<&mut Self>` 上運行。這使得我們可以繼續以 futures 0.2 的風格編寫程式,即無需引入 `Pin`/`Unpin` 或以其他方式談論 pin。一個關鍵要求是需要可交互運作性(interoperation),以便可以在任何需要 `Future` 的地方使用 `MoveFuture`。至少有兩種方法可以實現這種互操作: 383 | 384 | - 透過對 `T: MoveFuture` 的 `Future` 全面實作(blanket implementation)。這種方法目前阻止了一些**其他**所需的實作(特別圍繞在 `Box` 和 `&mut`),但這似乎不是問題的根本。 385 | 386 | - 透過子特徵的關係,使得 `T: Future` 本質上被定義為 `for<'a> Pin<&mut 'a T>: MoveFuture` 的別名。不幸的是,這種「更高等級」的特徵關係目前在特徵系統中效果不佳,而且這種方法在手動實現 `Future` 時也使事情變得更加複雜,收益相對較小。 387 | 388 | 該 RFC 採用的「一個核心特徵」方法的缺點是,它在手動編寫可移動 future 時與人因工程相衝突:您現在需要為您的型別引入 `Pin` 和 `Unpin`、調用 `poll_unpin` 和實現 `Unpin`。這一切都很機械化,但這是痛苦的。而 `Pin` 的人因工程學的改進可能會消除其中一些問題,但仍存在許多未解決的問題。 389 | 390 | 另一方面,雙特徵方法也有缺點。如果我們**還**移除錯誤型別,則會出現組合爆炸,因為我們最終需要每個特徵的 `Try` 變體(這也延伸到相關的特徵,例如 `Stream`)。更廣泛地說,使用單一特徵方法,`Unpin` 充當一種「獨立旋鈕」,可以與其他關注點作正交應用;使用雙特徵方法,它是「混合的」。目前這兩種雙特徵方法都受到了編譯器的限制,儘管這不應該被視為決定因素。 391 | 392 | **該 RFC 選擇單一特徵方法的主要原因是它是保守的、前向相容(forward-compatible)的選擇,並且已經在實際中證明了自己**。可以在未來的任何時候添加 `MoveFuture` 以及一個全面實作。因此,從本 RFC 中提出的單一 `Future` 特徵開始,在我們獲得經驗的同時,我們的選項也保持最大限度的開放。 393 | 394 | ## 喚醒設計 (Waker) 的基本原理、缺點和替代方案 395 | 396 | 該提議的先前疊代包括一個單獨的喚醒型別 `LocalWaker`,它是 `!Send + !Sync` 並且可用於實作最佳化的執行器行為,而無需原子引用計數或原子喚醒。然而,實際上,這些相同的最佳化可以使用執行緒本地喚醒佇列(queue)來實作,攜帶 ID 而不是指向喚醒物件的指標,並追蹤執行程序 ID 或執行緒 ID 以執行未發送 `Waker` 的 runtime 斷言執行緒。舉個簡單的例子,零原子的單執行緒鎖定執行器可以如下實現: 397 | 398 | ```rust 399 | struct Executor { 400 | // map from task id (usize) to task 401 | tasks: Slab, 402 | // list of woken tasks to poll 403 | work_queue: VecDeque, 404 | } 405 | 406 | thread_local! { 407 | pub static EXECUTOR: RefCell> = ...; 408 | } 409 | 410 | static VTABLE: &RawWakerVTable = &RawWakerVTable { 411 | clone: |data: *const ()| RawWaker { data, vtable: VTABLE, }, 412 | wake: |data: *const ()| EXECUTOR.borrow_mut().as_mut().expect(...).work_queue.push(data as usize), 413 | drop, 414 | }; 415 | ``` 416 | 417 | 雖然此解決方案向 `LocalWaker` 方法提供了較差的錯誤訊息(因為在錯誤的執行緒上發生 `wake` 之前,它不会恐慌,而不是在 `LocalWaker` 轉換為 `Waker` 時發生恐慌),它極大地透過去除 `LocalWaker` 和 `Waker` 型別的重複,簡化了給使用者的 API 。 418 | 419 | 在實際情況下,非常可能 Rust 生態系統中最常見的執行器繼續與多執行緒相容(就像現在一樣),因此在更特化的情況,針對這種情況最佳化其人因工程學,優先於更好的錯誤訊息。 420 | 421 | # 現有技術 422 | [現有技術]: #prior-art 423 | 424 | 以 async/await 語法和以 future(又名 promise)為基礎的大量現有技術。提議的 futures API 是受到 Scala 的 futures 的影響,並且與各種其他語言的 API 大體相似(就以提供的轉接器而言)。 425 | 426 | 此 RFC 中模型的獨特之處在於使用了任務,而不是 callback。RFC 的作者不知道其他 *future* 函式庫是否使用這種技術,但它是 functional programming 中一種相當知名且更普遍的技術。有關於最近的範例,請參閱這篇關於 Haskell 平行的[論文](https://www.microsoft.com/en-us/research/wp-content/uploads/2011/01/monad-par.pdf)。這個 RFC 似乎是一个將 「trampoline」 技術與明確的、開放式的任務/喚醒模型结合起来的新想法。 427 | 428 | # 未解決的問題 429 | [未解決的問題]: #unresolved-questions 430 | 431 | 暫時沒有。 432 | -------------------------------------------------------------------------------- /text/2789-sparse-index.md: -------------------------------------------------------------------------------- 1 | - Feature Name: sparse_index 2 | - Start Date: 2019-10-18 3 | - RFC PR: [rust-lang/rfcs#2789](https://github.com/rust-lang/rfcs/pull/2789) 4 | - Tracking Issue: [rust-lang/rust#9069](https://github.com/rust-lang/cargo/issues/9069) 5 | - Translators: [[@weihanglo](https://github.com/weihanglo)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/3fec7e2e44884cff1a8f6c4a278bd6896ce04227/text/2789-sparse-index.md) 7 | - Updated: 2022-04-02 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 可透過 HTTP 選擇性下載 crates-io 索引,類於 Ruby 的 Bundler 的作法。傳輸方式從預先(ahead-of-time)Git clone 改變為 HTTP 按需下載。既有的索引結構和內容維持不變。重要的是,此提案可與靜態檔案協同運作,而不需客製化的伺服器端 API。 13 | 14 | # 動機 15 | [動機]: #動機 16 | 17 | 完整的 crate 索引相對龐大且下載慢。它還會隨著 crates.io 增長而增長,讓情況每況愈下。需要下載完整索引這件事拖累了首次使用 Cargo 的速度。尤其是在無狀態的 CI 環境下更是緩慢又浪費且只使用完整索引的非常小一部分,剩下全都給拋棄。快取索引在 CI 環境可謂困難重重(`.cargo` 目錄很巨)又經常低效(如:在 Travis Ci 上傳下載大型快取幾乎和下載新鮮索引一樣慢)。 18 | 19 | 目前索引的儲存資料格式和 git 協定搭配起來並不是很舒服。未壓縮的索引內容 tarball(截止 eb037b4863)佔 176MiB,`gzip -1` 需要 16MiB,而 `xz -6` 壓縮則要 10MiB。然 Git clone 卻回報下載了 215 MiB,比未壓縮的最新索引內容還大,且是壓縮後 tarball 的 **二十倍之多**。 20 | 21 | 將 git 歷史記錄 shallow clone 或 squash 只是暫時解。且不論事實上 [GitHub 指出它們不想支援大型儲存庫的 shallow clone](http://blog.cocoapods.org/Master-Spec-Repo-Rate-Limiting-Post-Mortem/),也不論 libgit2 尚未支援 shallow clone,實際上這作法仍未解決客戶端必須下載包含所有 crate 的整包索引。 22 | 23 | # 教學式解說 24 | [教學式解說]: #教學式解說 25 | 26 | 將索引作為純文字檔案,透過 HTTP 開放出來。開放既有的索引佈局排列也夠用(就像從 raw.githubusercontent.com 看到的長相),不過以 HTTP 來說 URL scheme 可能可以簡化。 27 | 28 | 欲了解 crate 資訊和解析依賴,Cargo(或其他客戶端)可對每個依賴建立請求,並送往已知的 URL 以取得需要的資訊,例如:`https://index.example.com/se/rd/serde`。對各個依賴來說,客戶端也會繼續請求其依賴資訊, 遞迴循環,直到取得所有依賴(並快取)到本機端。 29 | 30 | 請求依賴檔案是有辦法平行化運作的,所以依賴解析最差情況下的延遲會限制在依賴樹的最大深度。實務上最差情況非常少見,因為依賴通常會多次出現在樹中,因此可以提前發現並增加平行化機會。且,若已有 lock 檔案,所有列在其中的依賴也都可平行化核對處理, 31 | 32 | ## 離線支援 33 | 34 | 此提議的解法完全保留了 Cargo 離線工作的功能。(連線的情況下)欲取得 crate 必然要下載足夠的索引來使用,之後所有資料會快取留作離線使用。 35 | 36 | ## 減少頻寬使用 37 | 38 | Cargo 支援 HTTP/2,可有效率地處理多個相似的請求。 39 | 40 | 所有已獲取的依賴檔案可被快取,並透過條件式 HTTP 請求(`Etag` 或 `If-Modified-Since` 標頭)來避免重複下載未改變的檔案。 41 | 42 | 依賴的檔案易於壓縮,目前 `rustc-ap-rustc_data_structures` 內最大的檔案可用 Brotli 從 1MiB 壓縮到 26KiB。許多伺服器支援透明地提供預壓縮檔案(例如:可從帶有適當內容編碼標頭的 `rustc-ap-rustc_data_structures.gz` 提供 `/rustc-ap-rustc_data_structures` 的請求處理),因此索引可以保有高壓縮率但不必花費過多 CPU 以提供這些檔案。 43 | 44 | 就算是最糟的完整索引一個檔案一個檔案的下載狀況,比起 git clone 仍然使用更少頻寬(所有檔案分別壓縮加起來大小 39MiB)。 45 | 46 | 提供「增量式變更記錄」檔(在「未來展望」詳述)可以避免過多的條件式請求。 47 | 48 | ## 處理移除的 crate 49 | 50 | 有需要的話,本提議的解法可支援 crate 移除。當客戶端欲檢查一個已移除的 crate 的新鮮度,會向伺服器請求並獲得 404/410/451 HTTP 狀態。客戶端可以根據情況處理,並清理本機端的資料(甚至是 tarball 和原始碼已簽出的情況下)。 51 | 52 | 53 | 若客戶端對該移除 crate 不感興趣,就不會檢查之,但極大機率從來不會這樣做,也不會下載它。若主動清除已移除的 crate 快取資訊非常重要,則可以延伸「增量式變更記錄」來通知該移除。 54 | 55 | # 缺點 56 | [缺點]: #缺點 57 | 58 | * crates-io 計畫替索引加上加密簽章,作為 HTTP 上額外一層保護。對 git 索引來說加密驗證非常直觀,不過簽署鬆散的 HTTP 索引可能蠻有挑戰性的。 59 | * 在還沒有增量式變更記錄前,一個基本的實作,需要許多請求來更新索引。和 git fetch 相比會有更高的延遲。然而,在一些早期的效能比較下,若 CDN 支援足夠(>60)多的平行請求,會比 git fetch 更快。對 Github 上的索引來說,Cargo 有一個捷徑會檢查 master 分支是否改變。若有增量式變更記錄檔,同樣的捷徑可以透過條件式 HTTP 請求該變更記錄檔來達成(例如,檢查 `ETag` 或 `Last-Modified`)。 60 | * 此解法高效與否仰賴於建立多個平行小請求。支援 HTTP/2 的伺服器上做檢查是 HTTP/1.1 的兩倍快,但其實速度超越 HTTP/1.1 很合理。 61 | * 替代 registry 的功能目前已穩定,代表基於 git 索引協定也穩定且無法移除了。 62 | * 發送模糊搜尋索引的工具(例如 `cargo add`)可能需要發送數個請求,或是改用其他方法。URL 已全數正規化到小寫,所以不分大小寫的不需要額外的請求。 63 | 64 | # 原理及替代方案 65 | [原理及替代方案]: #原理及替代方案 66 | 67 | ## 查詢 API 68 | 69 | 顯而易見的替代方案是建立可供查詢依賴解析的伺服器端 API(例如提供依賴清單就回傳一個 lockfile 或類似的東西)。然而,這會需要在伺服器端跑依賴解析。維護這種至關重要的動態 API 給幾乎所有 Rust 使用者每天使用,比其靜態檔案更難花費更高。 70 | 71 | 本提議之解法並不需要伺服器端任何邏輯。索引可以放在靜態檔案 CDN 上,且非常容易快取或鏡像。不需要修改索引如何取出。標準版本的索引可以繼續以 git 儲存庫形式維持完整的歷史記錄。這能保持與過往 Cargo 版本的相容性,並且第三方工具可以使用當前的索引格式。 72 | 73 | ## 從 rustup 提供初始索引 74 | 75 | Rust/Cargo 的安裝可以打包初始版本的索引,這樣當 Cargo 運作時,就不需要從 git 下載整份索引檔,只需要做與種子版本差異更新。這個索引需要 rustup 更聰明地分別處理,避免在安裝或更新不同版本的 Cargo 時,造成重複下載索引多次。這會讓索引的下載和壓縮更好,讓做法可使目前的實作撐更久,但仍沒辦法解決索引不斷增長的問題。 76 | 77 | 本提議之解法更是伸縮自如,因為 Cargo 只要下載和快取「需要用到」的索引,未使用/棄用/垃圾 crate 不會造成任何花費。 78 | 79 | ## Rsync 80 | 81 | rsync 協定需要掃描並校驗原始碼與目標檔案的檢查碼異同,這需要許多不必要的 I/O,且需要 SSH 或在伺服器上運行的客製化系統服務(daemon),這限制了架設索引的選項。 82 | 83 | # 先驅技術 84 | [先驅技術]: #先驅技術 85 | 86 | https://andre.arko.net/2014/03/28/the-new-rubygems-index-format/ 87 | 88 | Bundler 曾經是預先下載完整索引,和 Cargo 一樣,直到它變得太大太大。然後它改用中心化的查詢 API,直到問題多到難以支援。然後就切換到增量式下載偏平檔案索引格式,和本提議解法相似。 89 | 90 | # 未解決問題 91 | [未解決問題]: #未解決問題 92 | 93 | * 如何設定索引(包含替代 registry)該從 git 或新的 HTTP 下載?目前的語法使用 `https://` URL 當作 git-over-HTTP。 94 | * 我們如何確保切換到 HTTP registry 不會導致 lock 檔案出現巨大差異? 95 | * 如何改寫當前的解析器,開啟平行取得索引檔案的功能?目前所有索引需要同時都可用,這杜絕了平行化的可能性。 96 | 97 | # 實作可行性 98 | 99 | 目前已有一個經過測試的實作,是以簡單「貪婪」演算法來取得索引檔案,在 https://github.com/rust-lang/cargo/pull/8890 ,並且證實了不錯的效能,尤其是全新的建構。該實驗性實作的 PR 建議一個修改解析器的做法,以移除不必要的貪婪取得階段。 100 | 101 | # 未來展望 102 | [未來展望]: #未來展望 103 | 104 | ## 增量式的 crate 檔案 105 | 106 | Bundler 替每個獨立的依賴檔案使用僅允許附加(append-only)格式,以盡可能只增量下載新版本資訊。Cargo 的格式幾乎就是 append-only(除了 yank),所以如果單一檔案成長到是個問題,應該要版本修復它。然而,當前最大的 crate `rustc-ap-rustc_data_structures` 天天發佈新版本,每個版本增加 44 位元組(壓縮後),所以就算十年後它也才 190KB(壓縮後),看起來並沒有駭人到需要解決。 107 | 108 | ## 增量式變更記錄 109 | 110 | 截至目前,本方案在每次更新索引時,都必須重驗證每個索引檔案的新鮮度,即使許多檔案根本沒變。執行 `cargo update` 會導致索引更新,不過也有其他時機會導致更新,例如專案缺少 lockfile,或是添加了新的依賴。雖然 HTTP/2 pipeline 和條件式 GET 請求使得請求多個未改變檔案[不錯有效率](https://github.com/rust-lang/cargo/pull/8890#issuecomment-737472043),但如果我們能避免那些無謂的請求,只請求有更改的檔案會更好。 111 | 112 | 一個解決方案是提供一個索引的概覽,讓客戶端可以快速確認本機端的索引檔是否過期。為了避免客戶端無謂的請求完整引樹快照,索引可以維護一個僅允許附加(append-only)的變更記錄。當改變發生(crate 版本發佈或 yank),記錄檔會附加新的一條記錄:epoch number(下面解釋之)、最後修改時間戳記、變更的 crate 名稱,未來需要也可添加其他額外資訊。 113 | 114 | 因為這個記錄檔只允許附加,所以客戶端可以利用 `Range` HTTP 請求漸增地更新它。客戶端不必下載完整的記錄檔來用,下載從任意一點到檔案結束這段即可,這用 `Range` 請求來達成簡單易懂。當在記錄裡(從最末端開始)找到一個 crate,並且其更動時間和本機快取一致,如此客戶端就不需要額外對該檔案發送 HTTP 請求。 115 | 當記錄檔成長過大,epoch number 遞增,記錄檔就可以重設到空白。即使在客戶端對該新記錄檔的 `Range` 請求合法的狀況下,epoch number 仍可讓客戶端有辦法偵測記錄檔是否有重設。 116 | 117 | 最終,這個 RFC 並沒有建議此方案,因為記錄檔恐導致[程式碼變得十分複雜](https://github.com/rust-lang/cargo/commit/bda120ad837e6e71edb334a44e64533119402dee),且[比起簡單的下載正面效益不大](https://github.com/rust-lang/cargo/pull/8890#issuecomment-738316828)。若索引快照跟著 registry signing 一起實作了,此 RFC 的實作就可利用該快照機制當作變更記錄。 118 | 119 | ## 應付不一致的 HTTP 快取 120 | 121 | 索引並不需要將所有檔案合併產生一個快照。索引是一次更新一個檔案,並且只需保留部分更新排序。從 Cargo 的角度來看,各個依賴可允許獨立更新。 122 | 123 | 過期的快取僅在於新版本的 crate 使用了最新版本且剛發佈的依賴時,且該 crate 的快取失效得比其依賴早,才可能產生問題。Cargo 要求依賴需在可見的索引上有充分版本資訊,並且不會發佈任何「毀損」的 crate。 124 | 125 | 然而,CDN 快取總是有機會過期或失效的順序不一致。若 Cargo 偵測到索引的快取過期了(例如一個 crate 的依賴尚未出現在索引中),它可從該狀況復原,附加「快取破壞者」(如當前時間戳記)在 URL 上,以重新向索引請求檔案。就算 CDN 並不理會請求中的 `cache-control: no-cache`,這還是可靠能繞過過期快取的方法。 126 | -------------------------------------------------------------------------------- /text/2843-llvm-asm.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `llvm_asm` 2 | - Start Date: 2019-12-31 3 | - RFC PR: [rust-lang/rfcs#2843](https://github.com/rust-lang/rfcs/pull/2843) 4 | - Rust Issue: [rust-lang/rust#70173](https://github.com/rust-lang/rust/issues/70173) 5 | - Translators: [[@TSOTSI1](https://github.com/TSOTSI1)] 6 | - Commit: [The commit link this page based on](https://github.com/Amanieu/rfcs/blob/9911615f81471229684b0ab704409c9fcae55ede/text/2843-llvm-asm.md) 7 | - Updated: 2022-06-29 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 棄用現有的 `asm!` 巨集(macro),並提供一個名為 `llvm_asm!` 的相同巨集(macro)。功能開關也從 `asm` 重新命名為 `llvm_asm` 。 13 | 與 `asm!` 不同的是, `llvm_asm!` 不打算成為穩定版。 14 | 15 | # 動機 16 | [動機]: #動機 17 | 18 | 這個變更將 `asm!` 巨集(macro)釋放了,使它可用於內嵌組合語言項目組新設計的 `asm!` 巨集(macro), 19 | 同時為現在使用 `asm!` 的使用者提供一個簡單的方法來保持程式碼的正常運作。 20 | 21 | 對尚未支援新的 `asm!` 巨集(macro)的架構,在(nightly 版本)上執行內嵌組合語言可能會有用。 22 | 23 | # 教學式解說 24 | [教學式解說]: #教學式解說 25 | 26 | Rust 團隊目前正在重新設計 `asm!` 巨集(macro)。您應該將所有使用 `asm!` 的程式碼都替換為 `llvm_asm!` ,以避免程式碼在實施新的 `asm!` 巨集(macro)時毀壞。 27 | 28 | # 技術文件式解說 29 | [技術文件式解說]: #技術文件式解說 30 | 31 | 編譯器內所有對 `asm!` 的參考都將改參考 `llvm_asm!` 。 32 | `asm!` 將變成一個單純(已棄用)的 `macro_rules!` ,它會重新導到`llvm_asm!`。 33 | 棄用警告將告知使用者 `asm!` 將來的語義會作改變,並邀請他們使用 `llvm_asm!` 來替代。`llvm_asm!` 巨集(macro)將由 `llvm_asm` 功能開關來把控。 34 | 35 | # 缺點 36 | [缺點]: #缺點 37 | 38 | 此變更可能需要人為變更兩次程式碼:首先變更為 `llvm_asm!` ,然後再實施新的 `asm!` 巨集(macro)。 39 | 40 | # 原理及替代方案 41 | [原理及替代方案]: #原理及替代方案 42 | 43 | 我們可以跳過棄用期,並同時執行重新命名新的 `asm!` 巨集(macro)。 44 | 總之用 Rust(nightly 版本)保證能一次破壞大量程式碼,就無需任何過渡期。 45 | 46 | # 先驅技術 47 | [先驅技術]: #先驅技術 48 | 49 | D 語言也支援兩種形式的內嵌組合語言。[first one][d-asm] 提供用於內嵌組合語言的嵌入式 DSL,它可以在不用 Clobber 情況下直接存取範圍內的變數,但只能在x86和x86_64的架構上使用。 50 | [second one][d-llvm-asm] 是 LLVM 內部內嵌組合語言句法的 RAM 介面,但它只適用於 DSL 的後端架構。 51 | 52 | [d-asm]: https://dlang.org/spec/iasm.html 53 | [d-llvm-asm]: https://wiki.dlang.org/LDC_inline_assembly_expressions 54 | 55 | # 未解決問題 56 | [未解決問題]: #未解決問題 57 | 58 | 無 59 | 60 | # 未來展望 61 | [未來展望]: #未來展望 62 | 63 | 當下執行的會在 [new `asm!` macro][inline-asm-rfc] 被執行後替換掉,這會破壞那些尚未轉換 `llvm_asm!` 的程式碼。 64 | 由於運算元分隔符將從 `:` 更改為 `,` ,所以不會有靜默的錯誤編譯,新的 `asm!` 巨集(macro)會出現語法錯誤,來保證現有 `asm!` 的任何呼叫都會失敗, 65 | 66 | [inline-asm-rfc]: https://github.com/rust-lang/rfcs/pull/2873 67 | -------------------------------------------------------------------------------- /text/3128-io-safety.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `io_safety` 2 | - Start Date: 2021-05-24 3 | - RFC PR: [rust-lang/rfcs#3128](https://github.com/rust-lang/rfcs/pull/3128) 4 | - Rust Issue: [rust-lang/rust#87074](https://github.com/rust-lang/rust/issues/87074) 5 | - Translators: [[@weihanglo](https://github.com/weihanglo)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/88b30e7a0b7ac5b0f52370a58b70c165a9a252af/text/3128-io-safety.md) 7 | - Updated: 2021-08-2o 8 | 9 | # 總結 10 | [總結]: #總結 11 | 12 | 透過引進**輸入輸出安全性**(I/O safety)之概念與一系列新型別與特徵,保障 `AsRaw` 及相關特徵的使用者對原始資源 handle (raw resource handle)之使用,以彌補 Rust 封裝邊界的漏洞。 13 | 14 | # 動機 15 | [動機]: #動機 16 | 17 | Rust 標準函式庫幾乎算是已經提供了**輸入輸出安全性**,保證程式的一部分若私自持有一個原始 handle (raw handle),其他部分就無法存取。例如 [`FromRawFd::from_raw_fd`] 標示為不安全,它不允許使用者在 safe Rust 之下執行如 `File::from_raw_fd(7)` 等操作,或是在程式各處私自持有檔案描述子(file descriptor)來執行輸入輸出。 18 | 19 | 不過仍有漏網之魚。許多函式庫的 API 透過接受 [`AsRawFd`]/[`IntoRawFd`] 來執行輸入輸出操作: 20 | 21 | ```rust 22 | pub fn do_some_io(input: &FD) -> io::Result<()> { 23 | some_syscall(input.as_raw_fd()) 24 | } 25 | ``` 26 | 27 | `AsRawFd` 並無限制 `as_raw_fd` 的回傳值,所以 `do_some_io` 最終會對任意 `RawRd` 的值執行輸入輸出。由於 [`RawFd`] 本身實作了 `AsRawFd`,甚至可以寫出 `do_some_io(&7)`。 28 | 29 | 這會使得程式[存取錯誤的資源],更甚者為其他地方私有的原始 handle 建立多個別名(alias),從而打破封裝的邊界,造成[遠處來的詭異行為]。 30 | 31 | 而在特殊情況下,違反輸入輸出安全性恐導致違反記憶體安全性。舉例來說,理論上替透過 Linux [`memfd_create`] 系統呼叫建立出來的檔案描述子打造 `mmap` 的安全封裝,並將之傳給 safe Rust 是可行的,畢竟它就是匿名的被開啟的檔案(anonymous open file),表示其他行程(process)無法存取之。然而,在沒有輸入輸出安全性且沒有永久封閉該檔案的情況下,該程式中其他程式碼恐意外地對該檔案描述子呼叫 `write` 或 `ftruncate`,進而打破記憶體安全性 `&[u8]` 不變的規則。 32 | 33 | 這個 RFC 透過以下幾點,開闢一條逐步關閉此漏洞的道路: 34 | 35 | - 一個新概念:輸入輸出安全性。其概念會撰寫在標準函式庫文件中。 36 | - 一系列全新的型別與特徵。 37 | - 替 [`from_raw_fd`]/[`from_raw_handle`]/[`from_raw_socket`] 撰寫新文件,解釋就輸入輸出安全性而言,它們為何不安全,順便解決出現好[幾][幾][次][次]的相同問題。 38 | 39 | [幾]: https://github.com/rust-lang/rust/issues/72175 40 | [次]: https://users.rust-lang.org/t/why-is-fromrawfd-unsafe/39670 41 | [存取錯誤的資源]: https://cwe.mitre.org/data/definitions/910.html 42 | [遠處來的詭異行為]: https://en.wikipedia.org/wiki/Action_at_a_distance_(computer_programming) 43 | 44 | # 教學式解說 45 | [教學式解說]: #教學式解說 46 | 47 | ## 輸入輸出安全性概念 48 | 49 | Rust 標準函式庫提供了低階型別,以表示原始的作業系統資源 handle:類 Unix 平台的 [`RawFd`] 和 Windows 上的 [`RawHandle`] 與 [`RawSocket`]。然而,它們並沒有提供任何自身的行為,而是僅作為一個標識符(identifier),並在低階的作業系統 API 間傳遞。 50 | 51 | 這些原始 handle 可以視為原始指標(raw pointer)且具有相同的危險性。雖然**取得**一個原始指標是安全的,但當該原始指標是非法指標,或是比其指向記憶體之處活得更久時,對原始指標**取值**(dereference)都可能引發未定義行為(undefined behavior)。無獨有偶,透過 [`AsRawFd::as_raw_fd`] 或類似方法**取得**一個原始 handle 是安全的,但當它並非合法 handle 或在關閉之後才拿來用時,用該 handle 來執行輸入輸出恐導致「損毀的輸出結果」、「遺失或洩漏輸入資料」或「違反封裝的邊界」。在這兩個案例中,影響不僅限於本地,也會影響程式的其他部分。保護原始指標免於危險稱作記憶體安全性,所以保護原始 handle 免於危險我們叫它**輸入輸出安全性**。 52 | 53 | Rust 標準函式庫也有高階型別如 [`File`] 和 [`TcpStream`] 來提供高階的作業系統 API 界面,這些型別包裝了原始 handle。 54 | 55 | 這些高階型別同時實作了在類 Unix 平台的 [`FromRawFd`] 特徵,以及 Windows 上的 [`FromRawHandle`]/[`FromRawSocket`] 。這些特徵提供許多函式將底層的值封裝產生出高階的值。由於這些函式無法確保輸入輸出的安全性,因此標示為不安全。型別系統並不會限制這些型別傳入: 56 | 57 | ```rust 58 | use std::fs::File; 59 | use std::os::unix::io::FromRawFd; 60 | 61 | // 建立一個檔案。 62 | let file = File::open("data.txt")?; 63 | 64 | // 從任意整數值構建一個 `File`,這個型別能通過檢查,但 7 可能在執行期間無法 65 | // 被識別為任何活生生的資源,或它可能不慎指向程式其他地方封裝好的原始 handle 66 | // 一個 `unsafe` 區塊告知呼叫者對此有責任,需使其免於這些風險。 67 | let forged = unsafe { File::from_raw_fd(7) }; 68 | 69 | // 取得一個 `file` 內部的原始 handle 的副本。 70 | let raw_fd = file.as_raw_fd(); 71 | 72 | // 關閉 `file`。 73 | drop(file); 74 | 75 | // 開啟一些無關的檔案。 76 | let another = File::open("another.txt")?; 77 | 78 | // 其他對 `raw_fd`(也就是 `file` 內部的原始 handle)的使用有可能使其生命週 79 | // 期長於與作業系統的關聯。這恐導致意外建立別名指向其他封裝起來的 `File` 80 | // 實例,例如 `another`。因此,一個 `unsafe` 區塊告知呼叫者對此有責任,需使 81 | // 其免於這些風險。 82 | let dangling = unsafe { File::from_raw_fd(raw_fd) }; 83 | ``` 84 | 85 | 呼叫端必須確保傳入 `from_raw_fd` 的值一定是從作業系統回傳所得,並且 `from_raw_fd` 的回傳值的生命週期不會長於和作業系統關聯的 handle。 86 | 87 | 雖然將輸入輸出安全性作為明確的概念是個新作法,但其實它也反映出許多常見的實踐。除了引入一些新型別與特徵及其實作外,Rust `std` 並不需改變已經穩定的界面。在推行之初,並不需讓整個 Rust 生態系馬上支援輸入輸出安全性,而是可以漸進式地採用之。 88 | 89 | ## `OwnedFd` 與 `BorrowedFd<'fd>` 90 | 91 | 這兩個型別概念上將取代 `RawFd`,並分別表示擁有和借用的 handle 值。`OwnedFd` 擁有一個檔案描述子,當 `OwnedFd` 釋放時就會關閉其檔案描述子。`BorrowedFd` 的生命週期標示該檔案描述子被借用多久。這些型別皆會自動實施其輸入輸出安全性不變的規則。 92 | 93 | 至於在 Windows 上,會以 `Handle` 和 `Socket` 形式呈現相應的型別。 94 | 95 | 這些型別在輸入輸出扮演的角色可類比 Rust 既有的記憶體管理相關型別: 96 | 97 | | 型別 | 類似於 | 98 | | ---------------- | ------------ | 99 | | `OwnedFd` | `Box<_>` | 100 | | `BorrowedFd<'a>` | `&'a _` | 101 | | `RawFd` | `*const _` | 102 | 103 | 不過兩者還是有差,輸入輸出安全性並不區分可不可變。在 Rust 的掌控之外,作業系統資源能以各種形式共享,所以輸入輸出可以視為使用了[內部可變性]。 104 | 105 | [內部可變性]: https://doc.rust-lang.org/reference/interior-mutability.html 106 | 107 | ## `AsFd`、`Into` 與 `From` 108 | 109 | 這三個型別概念上,在大多數用例中分別取代 `AsRawFd::as_raw_fd`、`IntoRawFd::into_raw_fd`,以及 `FromRawFd::from_raw_fd`。它們依據 `OwnedFd` 及 `BorrowedFd` 來運作,所以也會自動實施其輸入輸出安全性不變的規則。 110 | 111 | 使用這些型別後,就能避免在[動機]一節的 `do_some_io` 範例中提及的問題。由於只有合理擁有或借用檔案描述子的型別能實作 `AsFd`,所以這個版本的 `do_some_io` 不需要擔心偽造或迷途(dangling)的檔案描述子傳入。 112 | 113 | ```rust 114 | pub fn do_some_io(input: &FD) -> io::Result<()> { 115 | some_syscall(input.as_fd()) 116 | } 117 | ``` 118 | 119 | 至於在 Windows 上,會以 `Handle` 和 `Socket` 形式呈現相應的型別。 120 | 121 | ## 漸進式採用 122 | 123 | 輸入輸出安全性及其新型別與新特徵並不需要一次性全面導入,而是能夠分階段漸進採用: 124 | 125 | - 首先,在 `std` 新增這些新型別與新特徵,並替相關的型別實作之。這是向下相容的改變。 126 | - 在此之後,crate 可以開始用這些新型別,並替 crate 自己的型別實作這些新特徵。這些改變相對小,而且符合語意化版號相容性,不需其他特殊處理。 127 | - 當標準函式庫和夠多的熱門 crate 實作這些新特徵後,其他 crate 可按照它們的開發步調,將這些新特徵作為泛型引數的限定條件(bound)。雖然這些改變不符合語意化版號的相容性,不過多數 API 使用者改用新特徵時並不需改變程式碼。 128 | 129 | # 技術文件式解說 130 | [技術文件式解說]: #技術文件式解說 131 | 132 | ## 輸入輸出安全性概念 133 | 134 | Rust 語言除了有記憶體安全性之外,Rust 標準函式庫同時提供了對輸入輸出安全性的保證。一個合法的輸入輸出操作,其所操作的原始 handle([`RawFd`]、 [`RawHandle`]、[`RawSocket`])必為明確從作業系統回傳之值,且這些操作僅發生在與作業系統關聯之生命週期內。當一段 Rust 程式碼宣稱輸入輸出安全性,代表該程式碼不可能導致非法的輸入輸出操作。 135 | 136 | 雖然有些作業系統的文件中說明其檔案描述子的配置演算法,但從這些演算法旁敲側擊出來的 handle 值並不會視為「明確從作業系統回傳之值」。 137 | 138 | 對接受任意原始輸入輸出 handle 值([`RawFd`]、[`RawHandle`]、[`RawSocket`])的函式,若安全的 API 會藉由這些 handle 執行輸入輸出,應標示為 `unsafe`。 139 | 140 | ## `OwnedFd` 與 `BorrowedFd<'fd>` 141 | 142 | `OwnedFd` 與 `BorrowedFd` 皆為 `repr(transparent)`,並帶有一個 `RawFd` 值,且兩者皆可應用區位最佳化(niche optimizations),所以和 `Option` 與 `Option>` 的大小相同,而且可在 FFI 宣告的函式中使用,例如 `open`, `read`, `write`, `close` 等。若以上述方法使用之,它們將確保 FFI 邊界的輸入輸出安全性。 143 | 144 | 這些型別同時會實作既有的 `AsRawFd`、`IntoRawFd`,以及 `FromRawFd` 特徵,所以它們可和既有程式碼的 `RawFd` 型別交互使用。 145 | 146 | ## `AsFd`、`Into` 與 `From` 147 | 148 | 這些型別提供 `as_fd`、`into` 與 `from` 函式,類似於 `AsRawFd::as_raw_fd`、`IntoRawFd::into_raw_fd` 與 `FromRawFd::from_raw_fd`。 149 | 150 | ## 原型實作 151 | 152 | 上述所有原型放在: 153 | 154 | 155 | 156 | README.md 有文件鏈結、範例、和當前提供類似功能的 crate 之調查研究。 157 | 158 | # 缺點 159 | [缺點]: #缺點 160 | 161 | Crate 若用到檔案描述子,如 [`nix`] 或 [`mio`],將需要遷移到有實作 `AsFd` 的型別,或將這類函式標示為不安全。 162 | 163 | crates 若用 `AsRawFd` 或 `IntoRawFd` 來接收任何「類檔案」或「類 socket」型別,如 [`socket2`] 的 [`SockRef::from`],將需換成 `AsFd` 或 `Into`,或將這類函式標示為不安全。 164 | 165 | # 原理及替代方案 166 | [原理及替代方案]: #原理及替代方案 167 | 168 | ## 有關「unsafe 僅為了記憶體安全性」 169 | 170 | Rust 有個慣例:`unsafe` 只適用於標示記憶體安全性。舉個有名的案例, [`std::mem::forget`] 曾標示為不安全,但後來[被改為安全],且其結論指出 unsafe 僅該用作標示記憶體安全性,而不該用來標示可能作繭自縛的情況或恫嚇應避免使用的 API 上。 171 | 172 | 記憶體安全性造成的危害比其他程式設計面向更甚,它不僅要避免意外行為發生,且仍需避免無法限制一段程式碼能做什麼。 173 | 174 | 輸入輸出安全性也落在這個範疇,有兩個因素: 175 | 176 | - (若作業系統存在 `mmap` 相關 API)在安全封裝的 `mmap` 中,輸入輸出安全性的錯誤仍會導致記憶體安全性的錯誤。 177 | - 輸入輸出安全性之錯誤也意味著一段程式碼可以在沒有通知或被授予任何引用的情形下,讀寫或刪除程式其他部分正在使用的資料。這使得在無法通曉一個 crate 鏈結到的其他 crate 的所有實作細節下,非常難以限制這個 crate 可以做什麼。 178 | 179 | 原始 handle 更像指向單獨的位址空間(address space)的原始指標,它們可能迷途(dangle)或造假。輸入輸出安全性近似於記憶體安全性,兩者皆竭力杜絕遠處來的詭異行為(spooky-action-at-a-distance),且對兩者來說,所有權都可作為建立穩健抽象化的主要根基,所以自然而然共用了相似的安全性概念。 180 | 181 | [`std::mem::forget`]: https://doc.rust-lang.org/std/mem/fn.forget.html 182 | [被改為安全]: https://rust-lang.github.io/rfcs/1066-safe-mem-forget.html 183 | 184 | ## 將輸入輸出 Handle 當作純資料 185 | 186 | 主要的替代方案的說法是原始 handle 為純資料(plain data),並沒有輸入輸出安全性,和作業系統資源的生命週期也無任何與生俱來的關係。至少在類 Unix 平台,這些永遠不會導致記憶體不安全或是未定義行為。 187 | 188 | 不過,大部分的 Rust 程式碼不直接與原始 handle 互動。撇開本 RFC 不談,不與原始 handle 互動是件好事。所謂資源,一定會有生命週期,若大部分的 Rust 程式碼能使用各方面都更易上手又能自動管理生命週期的更高階的型別,這樣鐵定更棒。不過,純資料的方案對於相對不常見的案例,最多只能讓原始 handle 的操作容易撰寫些。這可能只是蠅頭小利,甚至可能是個缺點,有可能最後變相鼓勵大家在不需要時去用了原始 handle 。 189 | 190 | 純資料的方案亦不需要變更任何 crate 的程式碼。而輸入輸出安全性的方案則需改動如 [`socket2`]、[`nix`] 和 [`mio`] 這些用到 [`AsRawFd`] 和 [`RawFd`] 的 crate,不過這改動可以漸進推廣到整個生態系,而不必一次性完成。 191 | 192 | ## `IoSafe` 特徵(與它的前身 `OwnsRaw`) 193 | 194 | 這個 RFC 在早先幾個版本提議過一個 `IoSafe` 特徵,這個特徵會帶來小程度但具侵入性的修復。來自該 RFC 的回饋促使一系列新型別與特徵的開發。這個開發牽扯更廣的 API 範圍,也意味著需要更多設計和審核。並且隨著時間推移,整個 crate 生態系需要更大規模的改動。然而,早期跡象指出,本 RFC 引入的新型別與特徵更易理解,使用上更順手且安全,所以長期來說有更穩健的基礎。 195 | 196 | `IoSafe` 早期叫做 `OwnsRaw`。我們很難替這個特徵找到恰到好處的名字,這也許是個訊號,表示它並非良好設計的特徵。 197 | 198 | # 先驅技術 199 | [先驅技術]: #先驅技術 200 | 201 | 大部分記憶體安全的程式語言都對原始 handle 做了安全的抽象層。多數情況下,它們僅是簡單地避免暴露原始 handle,例如 [C#]、[Java] 等。若將透過原始 handle 執行輸入輸出標示為不安全,可讓 safe Rust 與這些程式語言達到相同程度的安全保證。 202 | 203 | 在 crates.io 上有好幾個 crate 封裝了擁有或借用的檔案描述子。[io-lifetimes 的 README.md 中 Prior Art 一節]詳細描述了其與其他既有 crate 的同異。從高層次角度來看,既有的 crate 都與 io-lifetimes 共享相同的基本概念。這些 crate 都圍繞著 Rust 生命週期與所有權概念打造,而這恰恰說明這些概念非常適合這個問題。 204 | 205 | Android 有特殊的 API 會偵測不恰當的 `close`,詳見 [rust-lang/rust#74860](https://github.com/rust-lang/rust/pull/74680)。這些 API 的動機就是一種輸入輸出安全性的應用。Android 的特殊 API 用了動態檢查,讓它們可以在跨來源語言的邊界實施這些規則。本 RFC 提議的輸入輸出安全性的型別和特徵只專注在 Rust 程式碼本身實施這些規則,所以它們可在編譯期間利用 Rust 的型別系統實施這些規則,而非延遲到執行期間。 206 | 207 | [io-lifetimes 的 README.md 中 Prior Art 一節]: https://github.com/sunfishcode/io-lifetimes#prior-art 208 | [C#]: https://docs.microsoft.com/en-us/dotnet/api/system.io.file?view=net-5.0 209 | [Java]: https://docs.oracle.com/javase/7/docs/api/java/io/File.html?is-external=true 210 | 211 | # 未解決問題 212 | [未解決問題]: #未解決問題 213 | 214 | ## 形式化所有權 215 | 216 | 此 RFC 並沒有為原始 handle 的所有權和生命週期定義一個形式化模型(formal model)。這 RFC 對原始 handle 規範之定位尚不明朗。當 handle 只是整數型別時,與其關聯資源之生命週期意義為何?所有具有相同值的整數型別會共享該關連嗎? 217 | 218 | Rust [參考手冊]根據 [LLVM 的指標別名規則]定義了記憶體的未定義行為;輸入輸出可能需要類似的 handle 別名規則。這對目前的實際需求而言似非必要,但未來可對此進行探索。 219 | 220 | [參考手冊]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 221 | 222 | # 未來展望 223 | [未來展望]: #未來展望 224 | 225 | 以下包含一些從此 RFC 延伸的可能想法: 226 | 227 | - Clippy 警吿常見的輸入輸出不安全狀況。 228 | - 一個原始 handle 所有權形式化模型。可想像為延伸 Miri 使其揪出「關閉後使用」和「使用偽造的 handle」這類錯誤。 229 | - 一個屬於 Rust,細緻且基於能力的安全模型(capabability-based security model)。藉由此模型提供的保證,在 safe Rust 語境下就不可能偽造出假的原始 handle 高階封裝。 230 | - 還可替有實作 `AsFd`、`Into` 或 `From` 的型別添加一些方便的功能: 231 | - `from_into_fd` 函式:取得 `Into` 並將之轉為 `From`,讓使用者一步執行這些常見的轉換步驟。 232 | - `as_filelike_view::()` 函式:回傳一個 `View`,其中包含內部檔案描述子構建出來的暫時實例 T,讓使用者能以 `File` 或 `TcpStream` 等方式查看原始的檔案描述子。 233 | - 簡單使用情景的可攜性。由於 Windows 有兩種不同的 handle 型別,但 Unix 只有一種,因此在這領域中達成可攜性並非易事。然而,在部分案例中,可將 `AsFd` 和 `AsHandle` 一視同仁,而另外一些情況則可以把 `AsFd` 和 `AsSocket` 當作相同的。在這兩類情形,普通的 `FileLike` 和 `SocketLike` 抽象化能讓程式碼泛用在 Unix 和 Windows 上。 234 | 235 | 類似的可攜性也能推廣到 `From` 及 `Into`。 236 | 237 | # 致謝 238 | [致謝]: #致謝 239 | 240 | 感謝 Ralf Jung ([@RalfJung]) 引導我理解這個主題至此,鼓勵我和審核這個 RFC 的草案,並耐心回答我諸多問題! 241 | 242 | [@RalfJung]: https://github.com/RalfJung 243 | [`File`]: https://doc.rust-lang.org/stable/std/fs/struct.File.html 244 | [`TcpStream`]: https://doc.rust-lang.org/stable/std/net/struct.TcpStream.html 245 | [`FromRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html 246 | [`FromRawHandle`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawHandle.html 247 | [`FromRawSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawSocket.html 248 | [`AsRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsRawFd.html 249 | [`AsRawHandle`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsRawHandle.html 250 | [`AsRawSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsRawSocket.html 251 | [`IntoRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.IntoRawFd.html 252 | [`IntoRawHandle`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.IntoRawHandle.html 253 | [`IntoRawSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.IntoRawSocket.html 254 | [`RawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/type.RawFd.html 255 | [`RawHandle`]: https://doc.rust-lang.org/stable/std/os/windows/io/type.RawHandle.html 256 | [`RawSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/type.RawSocket.html 257 | [`AsRawFd::as_raw_fd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsRawFd.html#tymethod.as_raw_fd 258 | [`FromRawFd::from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html#tymethod.from_raw_fd 259 | [`from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html#tymethod.from_raw_fd 260 | [`from_raw_handle`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle 261 | [`from_raw_socket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket 262 | [`std::mem::forget`]: https://doc.rust-lang.org/stable/std/mem/fn.forget.html 263 | [`SockRef::from`]: https://docs.rs/socket2/0.4.0/socket2/struct.SockRef.html#method.from 264 | [`unsafe_io::OwnsRaw`]: https://docs.rs/unsafe-io/0.6.2/unsafe_io/trait.OwnsRaw.html 265 | [LLVM 的指標別名規則]: http://llvm.org/docs/LangRef.html#pointer-aliasing-rules 266 | [`nix`]: https://crates.io/crates/nix 267 | [`mio`]: https://crates.io/crates/mio 268 | [`socket2`]: https://crates.io/crates/socket2 269 | [`unsafe-io`]: https://crates.io/crates/unsafe-io 270 | [`posish`]: https://crates.io/crates/posish 271 | [rust-lang/rust#76969]: https://github.com/rust-lang/rust/pull/76969 272 | [`memfd_create`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html 273 | -------------------------------------------------------------------------------- /text/3151-scoped-threads.md: -------------------------------------------------------------------------------- 1 | - Feature Name: scoped_threads 2 | - Start Date: 2019-02-26 3 | - RFC PR: [rust-lang/rfcs#3151](https://github.com/rust-lang/rfcs/pull/3151) 4 | - Rust Issue: [rust-lang/rust#93203](https://github.com/rust-lang/rust/issues/93203) 5 | - Translators: [[@FizzyElt](https://github.com/FizzyElt)] 6 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/6de9eb35193852dd910c38f5c2c41e6f3e2a8ed4/text/3151-scoped-threads.md) 7 | - Updated: 2022-07-05 8 | 9 | # 概要 10 | [概要]: #summary 11 | 12 | 在標準函式庫中新增作用域執行緒,其允許產生可借用父執行緒之變數的執行緒。 13 | 14 | 範例: 15 | 16 | ```rust 17 | let var = String::from("foo"); 18 | 19 | thread::scope(|s| { 20 | s.spawn(|_| println!("borrowed from thread #1: {}", var)); 21 | s.spawn(|_| println!("borrowed from thread #2: {}", var)); 22 | }); 23 | ``` 24 | 25 | # 動機 26 | [動機]: #motivation 27 | 28 | 在 Rust 1.0 發布之前,我們有與作用域執行續相同作用的 [`thread::scoped()`](https://docs.rs/thread-scoped/1.0.2/thread_scoped/),但後來發現一個健全性的問題,可能導致 use-after-frees,所以它已被移除。這一歷史事件稱為[洩密事件](http://cglab.ca/~abeinges/blah/everyone-poops/)。 29 | 30 | 幸運的是,舊的作用域執行緒可被修復,透過閉包(closure)而非守護(guard),來確保生成的執行緒會自動會合(join)。但我們對在 Rust 1.0 中加入的作用域執行緒並不放心,所以我們決定將其放在外部 crates 之中,並有可能在未來的某個時刻回到標準函式庫中。四年過去了,那個未來就是現在。 31 | 32 | 作用域執行緒在 [Crossbeam](https://docs.rs/crossbeam/0.7.1/crossbeam/thread/index.html) 中經過多年的經驗累積,我們的設計已經趨於成熟,足以被推廣到標準函式庫中。 33 | 34 | 更多內容請看[基本理由及替代方案](#rationale-and-alternatives)部分。 35 | 36 | # 教學式解說 37 | [教學式解說]: #guide-level-explanation 38 | 39 | 執行緒生成的 "hello world" 可能如下所示: 40 | 41 | ```rust 42 | let greeting = String::from("Hello world!"); 43 | 44 | let handle = thread::spawn(move || { 45 | println!("thread #1 says: {}", greeting); 46 | }); 47 | 48 | handle.join().unwrap(); 49 | ``` 50 | 51 | 現在讓我們嘗試生成兩個使用相同 greeting 的執行緒。不幸的是,我們必須克隆它,因為 [`thread::spawn()`](https://doc.rust-lang.org/std/thread/fn.spawn.html) 有 `F: 'static` 要求,這意味著執行緒不能借用局部變數: 52 | 53 | ```rust 54 | let greeting = String::from("Hello world!"); 55 | 56 | let handle1 = thread::spawn({ 57 | let greeting = greeting.clone(); 58 | move || { 59 | println!("thread #1 says: {}", greeting); 60 | } 61 | }); 62 | 63 | let handle2 = thread::spawn(move || { 64 | println!("thread #2 says: {}", greeting); 65 | }); 66 | 67 | handle1.join().unwrap(); 68 | handle2.join().unwrap(); 69 | ``` 70 | 71 | 作用域執行緒來解決!透過打開一個新的 `thread::scope()` 區塊,我們可以向編譯器證明在這作用域內產生的所有執行緒也會在這個作用域內結束生命。 72 | 73 | ```rust 74 | let greeting = String::from("Hello world!"); 75 | 76 | thread::scope(|s| { 77 | let handle1 = s.spawn(|_| { 78 | println!("thread #1 says: {}", greeting); 79 | }); 80 | 81 | let handle2 = s.spawn(|_| { 82 | println!("thread #2 says: {}", greeting); 83 | }); 84 | 85 | handle1.join().unwrap(); 86 | handle2.join().unwrap(); 87 | }); 88 | ``` 89 | 90 | 這意味著可以毫無顧忌地借用作用域之外的變數! 91 | 92 | 現在我們不必再手動會合執行緒,因為所有未會合的執行緒將在作用域結束時自動會合: 93 | 94 | ```rust 95 | let greeting = String::from("Hello world!"); 96 | 97 | thread::scope(|s| { 98 | s.spawn(|_| { 99 | println!("thread #1 says: {}", greeting); 100 | }); 101 | 102 | s.spawn(|_| { 103 | println!("thread #2 says: {}", greeting); 104 | }); 105 | }); 106 | ``` 107 | 108 | 當以這種方式利用自動會合時,請注意,如果任何自動會合的執行緒出現恐慌,`thread::scope()` 將出現恐慌。 109 | 110 | 你可能已經注意到作用域執行緒現在只接受一個參數,它只是對 `s` 的另一個引用。由於 `s` 存在於作用域內,我們不能直接借用它。使用傳遞的參數來生成巢狀執行緒: 111 | 112 | ```rust 113 | thread::scope(|s| { 114 | s.spawn(|s| { 115 | s.spawn(|_| { 116 | println!("I belong to the same `thread::scope()` as my parent thread") 117 | }); 118 | }); 119 | }); 120 | ``` 121 | 122 | # 技術文件式解說 123 | [技術文件式解說]: #reference-level-explanation 124 | 125 | 我們在 `std::thread` 模組中新增兩個新的型別: 126 | 127 | ```rust 128 | struct Scope<'env> {} 129 | struct ScopedJoinHandle<'scope, T> {} 130 | ``` 131 | 132 | 生命週期 `'env` 代表作用域外的環境,而 `'scope` 代表作用域本身。更準確地說,作用域外的所有內容都比 `'env` 和 `'scope` 內的所有內容都長。生命週期的關係是: 133 | 134 | ``` 135 | 'variables_outside: 'env: 'scope: 'variables_inside 136 | ``` 137 | 138 | 接下來,我們需要 `scoped()` 跟 `spawn()` 函式: 139 | 140 | ```rust 141 | fn scope<'env, F, T>(f: F) -> T 142 | where 143 | F: FnOnce(&Scope<'env>) -> T; 144 | 145 | impl<'env> Scope<'env> { 146 | fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> 147 | where 148 | F: FnOnce(&Scope<'env>) -> T + Send + 'env, 149 | T: Send + 'env; 150 | } 151 | ``` 152 | 153 | 這就是作用域執行緒的要點,真的。 154 | 155 | 現在我們只需要再做兩件事來使 API 完整。首先,`ScopedJoinHandle` 等同於 `JoinHandle`,但與 `'scope` 生命週期掛勾,所以它將有同樣的方法。第二,執行緒生成器需要能夠在一個作用域内生成執行緒。 156 | 157 | ```rust 158 | impl<'scope, T> ScopedJoinHandle<'scope, T> { 159 | fn join(self) -> Result; 160 | fn thread(&self) -> &Thread; 161 | } 162 | 163 | impl Builder { 164 | fn spawn_scoped<'scope, 'env, F, T>( 165 | self, 166 | &'scope Scope<'env>, 167 | f: F, 168 | ) -> io::Result> 169 | where 170 | F: FnOnce(&Scope<'env>) -> T + Send + 'env, 171 | T: Send + 'env; 172 | } 173 | ``` 174 | 175 | # 缺點 176 | [缺點]: #drawbacks 177 | 178 | 作用域執行緒的主要缺點是使標準函式庫有點大。 179 | 180 | # 基本原理和替代方案 181 | [基本原理和替代方案]: #rationale-and-alternatives 182 | 183 | * 將作用域執行緒保留在外部 crates 之中。 184 | 185 | 將他們放在標準函式庫有幾個優點: 186 | 187 | * 這是一個非常常見和實用的工具,非常適合學習、測試和探索性程式設計。每個學習 Rust 的人都會在某個時候遇到借用和執行緒的互動。有一個非常重要的教訓是執行緒實際上**可以**借用局部變數,但標準函式庫並沒有反映這一點。 188 | 189 | * 有些人可能會爭辯說我們應該完全不鼓勵使用執行緒,而是將人們指向像 Rayon 和 Tokio 這樣的執行器。但是,`thread::spawn()` 需要 F: `F: 'static` 並且無法繞過它,這感覺就像是標準函式庫中缺少的部分。 190 | 191 | * 實現作用域執行緒非常難處理,因此最好有標準函式庫提供一個可靠的解決方案。 192 | 193 | * 官方文檔和書籍中有許多範例可以透過作用域執行緒進行簡化。 194 | 195 | * 作用域執行緒通常是比 `thread::spawn()` 更好的預設值,因為它們確保生成的執行緒被連接並且不會意外 「洩漏」。這有時是單元測試中的一個問題,如果單元測試產生執行緒並忘記會合它們,「迷途」 的執行緒可能會積累。 196 | 197 | * 使用者一直在 IRC 和論壇上詢問作用域執行緒。將它們作為 `std::thread` 中的「祝福」模式對每個人都有好處。 198 | 199 | * 從 `scope` 回傳一個 `Result`,包括所有捕獲的恐慌。 200 | 201 | * 這很快變得複雜,因為多個執行緒可能已經恐慌。回傳 `Vec` 或其他恐慌的集合並不總是最有用的介面,而且通常是不必要的。如果使用者想要處理它們,在 `ScopedJoinHandle` 上顯式使用 `.join()` 來處理恐慌是最靈活且最有效的方法。 202 | 203 | * 不要將 `&Scope` 參數傳遞給執行緒。 204 | 205 | * 假如使用 `scope.spawn(|| ..)` 而不是 `scope.spawn(|scope| ..)`,則需要 `move` 關鍵字 (`scope.spawn(move || ..)`),如果你想在 closure 內使用作用域,將變得不符合人因工程。 206 | 207 | 208 | # 現有技術 209 | [現有技術]: #prior-art 210 | 211 | 自 Rust 1.0 以來,Crossbeam 就有了[作用域執行緒](https://docs.rs/crossbeam/0.7.1/crossbeam/thread/index.html)。 212 | 213 | Crossbeam 的作用域執行緒有兩種設計。舊的是在 `thread::scoped()` 被刪除後,我們想在 Rust 1.0 時代有一個合理的替代方案。新的則是在去年的大改版之中。 214 | 215 | * 舊: https://docs.rs/crossbeam/0.2.12/crossbeam/fn.scope.html 216 | * 新: https://docs.rs/crossbeam/0.7.1/crossbeam/fn.scope.html 217 | 218 | 新舊作用域執行緒之間存在一些差異: 219 | 1. `scope()` 現在從子執行緒傳播未處理的恐慌。在舊的設計中,恐慌被默默地忽略了。使用者仍然可以透過手動操作 `ScopedJoinHandle` 來處理恐慌。 220 | 221 | 2. 傳遞給 `Scope::spawn()` 的 closure 現在需要一個 `&Scope<'env>` 參數,該參數允許生成巢狀執行緒,這在舊設計中是不可能的。 Rayon 類似地傳遞了對子任務的引用。 222 | 223 | 3. 我們刪除了 `Scope::defer()`,因為它不是真的有用,有錯誤,並且有不明顯的行為。 224 | 225 | 4. `ScopedJoinHandle` 在 `'scope` 上進行了參數化,以防止它逃離作用域。 226 | 227 | Rayon 也有作用域,但它們在不同的抽象級別上工作——Rayon 產生任務而不是執行緒。它的 API 與本 RFC 中提出的 API 相同。 228 | 229 | # 未解決的問題 230 | [未解決的問題]: #unresolved-questions 231 | 232 | 這個概念可以延伸到非同步嗎?會有任何行為或 API 差異嗎? 233 | 234 | # 未來的可能性 235 | [未來的可能性]: #future-possibilities 236 | 237 | 在未来,我們也可以有一個像 Rayon 那樣的執行緒池,可以生成作用域内的任務。 -------------------------------------------------------------------------------- /text/3392-leadership-council/alternatives.md: -------------------------------------------------------------------------------- 1 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/075f4b30f5c33315163c8e6a75e3210af6229ded/text/3392-leadership-council/alternatives.md) 2 | 3 | # Rationale and alternatives 4 | 5 | The design space for governance is quite large. This section only attempts to address the largest and most consequential alternatives to the design decisions presented in this RFC. This section presents each such alternative along with justifications for why they were not selected. 6 | 7 | ## Broader governance changes in this RFC 8 | 9 | We considered doing *more* in this RFC to set up initial governance structures and improve existing governance structures. In particular, we considered changes to the existing set of top-level teams. 10 | 11 | However, we felt strongly that anything that *could* be deferred to the Council should be, and that this RFC should focus on defining and delimiting the Council itself and its interactions with the rest of the Project. We felt it would go beyond the mandate of the transitional leadership structure to do much more than just architecting long-term leadership. 12 | 13 | We also felt that further incremental evolutions would become much easier with the structures proposed by this RFC in place. 14 | 15 | We recognize that changes to the set of top-level teams will prove especially difficult. However, we felt that the interim leadership group (including top-level team leads) would have that problem in common with the Council. Furthermore, we found that many members and leads of top-level teams were if anything *enthusiastic* about potential systematic improvements in this area, rather than resistant to them, even when such changes involved their own teams. 16 | 17 | Apart from that, developing and building consensus on this RFC already represented a massive time investment by many people, and making it larger would make it take even longer. 18 | 19 | ## Alternative Council structures and non-representative Council members 20 | 21 | As an alternative to Council representatives exclusively being the representatives of top-level teams, we extensively considered other structures, whether in addition or in place of that. For instance, the Council could appoint additional members, or appoint successors, or some or all Council representatives could be elected by the Project. Such approaches could potentially make it easier to represent aspects or constituencies of the Project not *yet* represented by existing top-level teams, before even the nascent structures of those teams started to take shape. 22 | 23 | Specific variants we decided not to pursue: 24 | 25 | ### Non-representative Council structures 26 | 27 | Alternative structures in which Council members are not representatives of top-level teams would have various drawbacks: 28 | - Any structure that does not guarantee each team a representative would provide less comprehensive and balanced representation for existing teams. 29 | - A structure not based on team-appointed representatives would make it harder to change representatives quickly and easily in a pinch, such as in response to changes in personal circumstances, or changes in a representative's affiliations that cause a violation of the limits placed on shared affiliations. 30 | - Some variants of this (such as Council-appointed additional members or Council-appointed successors) would steer the Council towards a more self-perpetuating nature that we wanted to avoid. 31 | 32 | Ultimately, we addressed part of this issue by instead allowing the Council to easily create provisional teams (so as to introduce additional *representatives* on the Council), and then made room for the Council to further evolve its structure in the future by consent. 33 | 34 | ### Elections 35 | 36 | Any structure involving elections would raise additional problems: 37 | - Accurately determining the electorate: who precisely qualifies as being "part of the Rust Project"? 38 | - Many people have intuitive ideas about this, and such intuitions don't currently cause problems because we don't tie much of substance to that status. However, such intuitive definitions cause serious issues if membership in the Project determines eligibility to vote. 39 | - The usual problems of popularity contests: not all folks doing organizational/coordinative work are especially visible/glamorous/popular, and those doing visible/glamorous/popular work may serve the Project better doing that work rather than reallocating their time towards organizational/coordinative work. 40 | - Elections motivate some form of campaigning. 41 | - A robust election system would introduce more process complexity, both directly for the voting process, indirectly by making it harder to rotate/replace candidates in a pinch or supply alternates/backups. 42 | - Elections would introduce more difficult challenges when needing to change representatives quickly and easily in a pinch, such as in response to changes in personal circumstances, or changes in affiliation that run into the limits upon shared affiliations. The voters will have chosen candidates, and it's harder to go back to the electorate for new candidates, so there would have to be (for example) careful rules for selecting backup candidates based on the next lower number of votes. 43 | - Elections, no matter what voting system they use, inherently ignore the consent of many constituents. 44 | - Simpler election structures would not guarantee teams a representative, and would thus provide less comprehensive and balanced representation for existing teams. Providing more comprehensive/proportional representation of teams would add even more complexity to the election system. 45 | - In particular, if the people in the project fall into teams in a vaguely Pareto-style structure (a small number of teams contain a large number of people), a simple election structure may result in many teams having *no* representation. 46 | 47 | We felt that we could better improve people's routes to be heard and taken into account by ensuring all governance structures and all Project members are connected through parent teams, and thus that every member of the Project has at least one representative on the Council. 48 | 49 | ## Referendums 50 | 51 | We considered introducing a full-fledged referendum system, by which proposals could be introduced, supported, and voted on by the Project as a whole. This would sidestep issues of ensuring proposals get considered and added to the Council's agenda, and would make it easier to make substantial changes not aligned with the existing Council (for better or for worse); it would also serve as an addition check and balance on the Council. 52 | 53 | However: 54 | - This would have all the problems mentioned above about determining constituency in the Project. 55 | - This would also be a *complex* new structure introduced entirely in this RFC (rather than later by the Council). 56 | - This mechanism and its eligibility and corner cases would need to be *very* precisely specified, as it would often be invoked in situations with high tension and a need for a timely and authoritative decision. 57 | - Voting mechanisms, no matter what voting system they use, inherently ignore the consent of many constituents. 58 | - Voting mechanisms trend towards picking winners and losers, rather than consensus-seeking and finding ways to meet *everyone's* needs. 59 | - If such a mechanism were trivial to initiate, it could become a dysfunctional pseudo-"communication" mechanism in its own right, substituting for healthier communication and more consent-driven actions. It would, effectively, escalate problems into public dirty laundry, making it *harder* to resolve smaller problems. In addition, reporting on such events can generate unwarranted news like "Rust considers X" even if X has no meaningful support. 60 | - If such a mechanism were not trivial to initiate, the type of grassroots organizing required to successfully raise and pass such a referendum would produce better effects by working through teams, when the Project is well-aligned. 61 | - Conversely, if the Project has substantial issues aligning with its leadership, making *individual* decisions doesn't solve the underlying problem with Project health. 62 | 63 | We chose to instead provide extensive checks on the Council itself, and mechanisms to ensure feedback and alignment between the Council and the Project, as well as a last-resort mechanism, rather than providing an ongoing mechanism to make or override *individual* Project-wide decisions. 64 | 65 | ## Alternative checks and balances between the Leadership Council and the Project 66 | 67 | We considered many structures for additional checks and balances between the Leadership Council and the Project: 68 | - We considered "vote of no confidence" mechanisms, but these would have many of the same problems as referendums, including determining the electorate, being either too difficult or too easy to initiate, and tending towards escalation rather than resolution. 69 | - We considered arrangements in which members of teams could directly raise objections to Council RFCs. However, this added complexity for something that the consent decision-making mechanism *should* make redundant. 70 | - We considered more formal feedback systems that could provide checks on *individual* Council decisions. However, any such mechanisms would also make it difficult to make timely decisions, and the blocking mechanisms would cause problems if they were either too easy or too difficult to initiate. 71 | 72 | ## Alternative checks and balances between the Leadership Council and the moderation team 73 | 74 | We went through substantial tuning on the checks and balances between the Leadership Council and the moderation team: 75 | - We considered making audits not automatically granted, and instead having the Council decide whether to grant an audit request. However, this would raise fairness questions for how the Council decides when to grant an audit based on limited information, as well as motivating procedural delays to give time for such an evaluation. We also felt that automatic audits (at least initially) would provide an opportunity to thoroughly test and evaluate the audit process. 76 | - We also considered structures using separate auditors rather than using the "contingent moderators" as auditors, but this raised *severe* trust issues with sharing private moderation information with those auditors. 77 | 78 | ## Launching pad alternatives 79 | 80 | We considered other alternate structures apart from the "launching pad", for handling existing teams that aren't attached to the rest of the team structure. For instance, we considered attaching such teams directly to the Council; however, this would have required special-case handling for representation that would start to look a lot like the launching pad, but with more coordination work attached to the Council. 81 | 82 | We also considered options in which we *didn't* connect those teams, and permitted "disconnected" working groups and similar. This would require less transition, but would leave many Project members unrepresented and disenfranchised. 83 | 84 | We felt that we could best improve people's routes to be heard and taken into account by ensuring all governance structures and all Project members are connected through parent teams. 85 | 86 | We considered giving additional purviews to the launching pad, such as contributing to team organization and structure, best practices, or processes. However, the launching pad is already the one exception in which this RFC creates a new team, and we already have concerns about successfully staffing that team; we don't want to add further complexity beyond that in this RFC. The Council has the option of changing the launching pad's purview in the future. 87 | 88 | We considered the name "landing pad" (a place for unattached teams to land) instead of "launching pad", but we felt that "launching pad" better conveyed the idea of incubating teams and helping them thrive rather than just serving as a holding area. 89 | 90 | ## Double-linking 91 | 92 | We considered adopting a "double-linking" structure between the Council and top-level teams, in which teams have two representatives on the Council, one more responsible for connecting team-to-Council and the other more responsible for connecting Council-to-team. Such redundancy could provide a firmer connection between the Council and teams, making it *much* less likely that concerns from teams would fail to propagate to the Council and vice versa. However: 93 | - Such a structure would require either an unmanageable number of Council members or far fewer than our current number of top-level teams (and we did not want to change the number of top-level teams in this RFC, or limit the number of top-level teams that strongly). 94 | - This would require substantial changes to the structures of top-level teams themselves to permit such linking, and such structural changes would go beyond the remit of this RFC. 95 | - Some models of double-linking would have one of the representatives determined by the team and the other by the Council; such a model would add complexity to the membership of the Council, such that members were not exclusively the representatives of top-level teams, which would have many of the downsides of such variations mentioned above, notably giving the Council a more self-perpetuating nature. 96 | -------------------------------------------------------------------------------- /text/3392-leadership-council/initial-work-of-the-council.md: -------------------------------------------------------------------------------- 1 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/075f4b30f5c33315163c8e6a75e3210af6229ded/text/3392-leadership-council/initial-work-of-the-council.md) 2 | 3 | # Recommendations for initial work of the Council 4 | 5 | In the course of developing this RFC, and thinking extensively about the structure and operation of the Council, the interim leadership team also identified many other tasks that fell outside the scope of this RFC, and explicitly decided to defer those tasks to the new Council. This section documents those tasks, as a suggested starting point for bootstrapping the work of the Council. None of these are binding suggestions, and the Council can freely set and prioritize its own agenda; this section serves as a public, transparent handoff of knowledge and proposals to the Leadership Council. 6 | 7 | Some of these tasks represent meta-level decisions about the processes of the Council, and we chose not to make those decisions in this RFC to avoid enshrining a particular structure rather than deferring to those who will be working regularly with that structure. The remaining tasks represent a partial todo list of long-standing tasks that fall within the Council's purview, insofar as they have fallen through the gaps between team purviews. Some of these tasks should be delegated by the Council rather than worked on directly by the Council. The inclusion of a task in this list doesn't change what type of decision-making process is required for it; some of these may be Council-internal operational decisions or private operational decisions, while others will require a public policy process. 8 | 9 | These are in no particular order, other than that meta-level decisions about processes for making decisions will need to happen before decisions relying on those processes. 10 | 11 | ## Meta-level decisions about processes and policies 12 | 13 | - Determining where, when, and how frequently the Council meets. 14 | - Establishing processes for where the Council makes decisions, both synchronously (in meetings) and asynchronously. 15 | - Writing and agreeing on templates for decisions, that help guide the Council to remember and follow process steps. 16 | - Establishing specific processes around Council transparency, including records of decisions, minutes of meetings, the locations where these get published, and similar. 17 | - Establishing a process for appointing the "Project directors" to the Rust Foundation board in a timely fashion. The Council will need to make such appointments soon after formation, and will also need to help ensure continuity across the transition. 18 | - Establishing processes and conventions for the Council's regular review of its policy decisions. In particular, establishing expectations for the frequency of such reviews, with mechanisms to adjust those downwards when representatives express concern, or upwards after previous successful reviews. 19 | - Selecting tools and establishing processes for tracking the Council's backlog/todo list, making as much of that list as possible public for transparency, and having a well-defined mechanism for Project members or teams to ask the Council to address something (either publicly or privately). 20 | - Defining and documenting processes for external requests to the Council from outside the Project, ensuring they get routed appropriately, and taking steps where possible to ensure they can be directly routed to appropriate teams (potentially including new teams) in the future. 21 | - Bootstrapping the new "Launching Pad" team and ensuring it has enough structure to operate. 22 | - Organizing teams within Rust, and ensuring all teams and other governance structures are "attached" to appropriate places in the structure. (This includes working with teams to find appropriate homes, and ensuring such changes are ultimately reflected in the team metadata repository.) 23 | - Establishing and agreeing on processes for faster decision-making for simple one-off operational matters, such as responding to emails reaching out to the Project. 24 | - Ensuring the policy decision process (RFC process) is well-documented and linked from the Council documentation, so people know how Council public proposals happen. 25 | - Develop handoff procedures for handling transitions to new Council representatives, or to alternates. 26 | 27 | ## Other tasks/gaps potentially within the Council's purview 28 | 29 | - Checking in with teams and individuals across the Project, seeing what's going well and what needs help, and adding to the Council's todo list. 30 | - Checking for priority items that need *urgent* help from the Council. 31 | - Checking in with members of the former core team to identify items from their past todo lists and other open issues they're aware of, to add to the Council's todo list, and subsequently to either work on or delegate or otherwise disposition. 32 | - Checking in with the moderation team, to ensure they have sufficient support and resources to ensure growth and sustainability. Collaborating with the moderation team as they develop and codify their policies and procedures to better handle a broader range of situations across the Project. 33 | - Helping to develop plans to support understaffed or otherwise unsustainable teams. 34 | - Work with the infra team to develop a transition plan for privileges traditionally maintained by core (such as root privileges / logged-use break-glass credentials). Coordinate appropriate policies with infra. 35 | - Working with the Launching Pad team to help transition teams out of it into appropriate places in the organization. 36 | - Ensuring that top-level teams have well-documented purviews, starting to identify gaps between those purviews, and working with teams to determine when those gaps should fall to specific existing teams or become the purview of new teams. 37 | - Establishing policies to enable delegation of communication/PR tasks that have traditionally fallen to top-level leadership, and then making appropriate delegations of such work, potentially including the creation of teams. 38 | - Working with teams to establish coordination channels for team roadmaps, and developing processes to support cohesion across those roadmaps. 39 | - Making concrete plans to improve Rust Project diversity, including working with individual teams on how to better support diversity initiatives, as well as addressing gaps for which no individual team currently has responsibility. 40 | - Working with teams on processes for receiving feedback from subteams, particularly on proposed Council representatives. Particular attention should be paid to: 41 | - Ensure feedback is processed early, often, fairly, and consistently such that subteam members feel heard and Council members are given opportunity to address feedback and improve. 42 | - Help detect and address bias in Council representative selection, including status-quo bias towards existing Rust leaders or people similar to them. 43 | - Documenting and improving processes for interaction with the Rust Foundation, and considering organizational improvements to provide further ongoing support for those interactions (such as how and where Project directors fit into the organizational structure and how they interface with the Council regularly). 44 | - In particular, establishing the purview of the Project directors along with policies and procedures for ensuring proper representation of Project interests on the Foundation board. 45 | - Establishing proper procedures, and potentially teams, for handling issues which may have legal implications on the Project or Project participants. 46 | - Ensuring that people and teams within the Rust Project have access to appropriate training and resources for the positions they find themselves in, beyond the skills required for their direct purview. Foster a culture of team membership that values such skills and help teams find resources or training to bolster such skills. Such skills and training include, among many others: 47 | - General leadership and coordination skills, within a team and across a community 48 | - Transparent and legible reasoning skills, recognizing and documenting underlying values, crux-finding, and collaborative disagreement 49 | - Conflict resolution and de-escalation 50 | - Project management and planning 51 | - Communications between individuals, teams, and projects 52 | - Public communications 53 | - Help teams evaluate and consider replicating useful aspects of the Council's structure and processes within other teams across the Project (particularly top-level teams), such as: 54 | - Appropriate structures to help subteams collaborate and coordinate amongst themselves and with top-level teams 55 | - Structures for decision-making, including policies allowing for some types of decisions to be made more quickly, where appropriate 56 | - Transparency, privacy, and documentation of decisions 57 | - Policies for handling conflicts of interest among team members 58 | - Policies on the number of team members sharing a common affiliation 59 | - Ensure Project and Project member health by understanding and working against common work patterns where select "heroes" assume an outsized and unreasonable share of the maintenance burden by: 60 | - taking on large amounts of essential work that they do not really *want* to do because no one else volunteers 61 | - taking on so much work (either voluntarily or out of seeming necessity) that they are prone to burnout 62 | - taking on work no one else has the ability to do and for which the member's absence would lead to potential crises in the Project 63 | - Evaluating improvements to the RFC decision process, such as tracking and supporting multiple potential outcomes and changes in people's preferences without restarting decisions, and providing lighter-weight mechanisms for reversible decisions. 64 | 65 | -------------------------------------------------------------------------------- /text/3392-leadership-council/motivation.md: -------------------------------------------------------------------------------- 1 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/075f4b30f5c33315163c8e6a75e3210af6229ded/text/3392-leadership-council/motivation.md) 2 | 3 | # Motivation 4 | 5 | The Rust Project is composed of hundreds of globally distributed individuals each of whom have very different motivations for working on Rust. Rust's open culture allows for these individuals to collaborate in a productive manner where complex technical and organizational decisions are made through consensus building and stakeholder feedback. 6 | 7 | Rust's model for project management and decision-making delegates most decisions to the appropriate team. Teams are ultimately accountable for their purview. While teams' decision-making processes do not strictly need to be consensus based, stakeholder feedback is repeatedly solicited to ensure a plethora of opinions on each matter are considered and factored in. 8 | 9 | However, at times this leads to issues. These issues can be summarized by the following questions which do not have clear answers in Rust's current governance model: 10 | - What happens when it is unclear which teams' purviews a certain decision falls under? 11 | - Who is in charge of important but non-urgent work that is not clearly owned by an existing team? 12 | - Who is accountable when that work does not happen and organizational debt accrues? 13 | - How are teams in the Project held accountable to each other and to the wider Project? 14 | 15 | Examples of the type of work in question include the following. Please note that this list is far from exhaustive and is merely meant to give an impression of the type of work in question: 16 | - Helping identify gaps where existing teams are struggling to accomplish work or grow to meet challenges. 17 | - Establishing large structural changes such as the Rust Foundation or new top-level teams. 18 | - Project self-reflection. What aspects of Project operations are less than ideal? What do we do to change course? 19 | - Project-wide community management. While individual teams can ensure that their teams are welcoming places, how do we ensure that the Project as a whole is? 20 | - Policy work, for policies that have ramifications across the Project or even legal ramifications. 21 | - Ensuring teams coordinate their work so that the Rust Project produces results greater than the sum of the output of the individual teams. 22 | 23 | While the current system does at times lead to positive outcomes in the above scenarios, it often does not. Some examples of failure mode categories include: 24 | - No one is accountable for a decision and so the decision goes unmade, leaving it undefined. This requires solutions to repeatedly be developed either "off the cuff" or from first principles. This requires enormous amounts of energy and often leads to work not being done well or at all. In some cases it can even lead to burning out Project participants. 25 | - Much Project work that is non-urgent often does not get done. This can lead to processes and procedures that are done not because they are the best way to handle a situation but simply because they are the easiest. This can lead to outcomes that are unfair or even actively harmful to individuals. In general, working this way leads to a culture of "putting out fires" instead of actively fostering improvements. 26 | - The solutions to many of the issues facing the Rust Project require coordinated action across many different teams. Finding solutions for these issues requires investment at the organizational level, and it is often very difficult for individuals to coordinate and implement such structural investment. 27 | - Still, such Project work is often taken up by motivated individuals who then lack structural support for accomplishing goals leading to frustration and at times conflict and burnout. 28 | 29 | Historically, the core team delegated authority to "top-level" teams who have further delegated authority to subteams or other governance structures. However, since the work outlined above is often Project-wide and outside the purview of individual teams, delegation was sometimes difficult. As a result, the core team assumed the following two responsibilities: 30 | - Identifying, prioritizing, and advertising that certain important work needs to get done and does not fall under the purview of an existing team 31 | - Attempting to do that work 32 | 33 | Through experience by the core team, it has become clear that both the identification of problems *and* the actual work itself is far too much for a single team. This has led to less than ideal results and in some cases, burnout. While a small amount of work requires urgent and immediate action, the vast majority of work would benefit from being tracked and handled by dedicated governance structures. 34 | 35 | -------------------------------------------------------------------------------- /text/3392-leadership-council/non-goals.md: -------------------------------------------------------------------------------- 1 | - Commit: [The commit link this page based on](https://github.com/rust-lang/rfcs/blob/075f4b30f5c33315163c8e6a75e3210af6229ded/text/3392-leadership-council/non-goals.md) 2 | 3 | # Non-goals of this RFC 4 | 5 | The following are non-goals of this RFC. These may be met in future RFCs but are explicitly not part of this RFC. 6 | 7 | Non-goal #1: *Laying out the complete policies and procedures of the Council*. While the RFC lays out and bounds the structure of the Council, the Council's full policies and procedures will be created by the Council itself. It is also expected that the Council will change and adapt to meet the needs of the Rust Project as it evolves. 8 | 9 | Non-goal #2: *Addressing all governance and potential governance concerns*. One of the Council's responsibilities will be to identify and reflect on the issues present in governance, but we see the formation of the Council as part of a continuous process of improving Rust's leadership and how it meets the needs of the Project. 10 | 11 | Non-goal #3: *Forming additional teams*. The focus of this RFC is to form the Council and does not include the creation of additional teams, subteams, or groups of any kind. 12 | 13 | We recognize the importance of having additional teams, but see this as outside of the scope of this RFC. Instead, it will be the responsibility of the Council to investigate and understand such needs and then create additional teams to ultimately handle these issues. 14 | 15 | This has one exception, other than the Council itself: the "launching pad" top-level team, which provides a temporary grouping of teams not yet attached to any existing top-level team either directly or indirectly. 16 | 17 | Non-goal #4: *Altering the charters or purviews of existing teams*. While this RFC does discuss membership in the Council, it does not extend beyond this to update the charter or purview of any existing team. Existing teams continue to follow their existing charters and purviews. 18 | 19 | This has two exceptions: 20 | 21 | - the core team: As part of this RFC, all of the capabilities and responsibilities of the core team move to the Council and are then clarified, modified, and constrained by the rest of this RFC. 22 | - the moderation team: As this RFC covers topics like conflict resolution and Council oversight, it does define additional capabilities for the moderation team, as well as additional checks and balances providing bidirectional oversight between the moderation team and the Council. 23 | 24 | Non-goal #5: *Establishing completely immutable properties of the Council*. Any aspect established in this RFC can be modified in the future, via the public policy decision-making process, with oversight provided by that process. This RFC lays out policies for making such changes, and the processes of changing such policies must follow the existing policies. 25 | 26 | --------------------------------------------------------------------------------