├── .all-contributorsrc ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── Translators.md ├── book.toml ├── depoly.sh ├── package.json ├── scripts ├── fix-name.js ├── fix-reference.js └── fix-summary.js ├── src ├── MOVE语言术语中英文对照表.md ├── SUMMARY.md ├── abilities.md ├── abort-and-assert.md ├── address.md ├── bool.md ├── coding-conventions.md ├── conditionals.md ├── constants.md ├── diagrams │ ├── move_state.png │ └── solidity_state.png ├── equality.md ├── friends.md ├── functions.md ├── generics.md ├── global-storage-operators.md ├── global-storage-structure.md ├── integers.md ├── introduction.md ├── loops.md ├── modules-and-scripts.md ├── move-tutorial.md ├── packages.md ├── references.md ├── signer.md ├── standard-library.md ├── step_1 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── FirstModule.move ├── step_2 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── FirstModule.move ├── step_2_sol │ ├── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ │ └── FirstModule.move │ └── solution_commands ├── step_3 │ └── BasicCoin.move ├── step_4 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── step_4_sol │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── step_5 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── step_5_sol │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── step_6 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ ├── BasicCoin.move │ │ └── MyOddCoin.move ├── step_7 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── step_8 │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── step_8_sol │ └── BasicCoin │ │ ├── Move.toml │ │ └── sources │ │ └── BasicCoin.move ├── structs-and-resources.md ├── tuples.md ├── unit-testing.md ├── uses.md ├── variables.md └── vector.md └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "move-book-zh", 3 | "projectOwner": "move-dao", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": true, 11 | "commitConvention": "none", 12 | "skipCi": "true", 13 | "contributors": [ 14 | { 15 | "login": "Kusou1", 16 | "name": "zhang", 17 | "avatar_url": "https://avatars.githubusercontent.com/u/57334674?v=4", 18 | "profile": "https://github.com/Kusou1", 19 | "contributions": [ 20 | "code", 21 | "doc", 22 | "translation", 23 | "infra" 24 | ] 25 | }, 26 | { 27 | "login": "ruy1su", 28 | "name": "ruyisu", 29 | "avatar_url": "https://avatars.githubusercontent.com/u/9391802?v=4", 30 | "profile": "https://github.com/ruy1su", 31 | "contributions": [ 32 | "code", 33 | "translation", 34 | "doc", 35 | "review" 36 | ] 37 | }, 38 | { 39 | "login": "lshoo", 40 | "name": "lshoo", 41 | "avatar_url": "https://avatars.githubusercontent.com/u/670440?v=4", 42 | "profile": "https://github.com/lshoo", 43 | "contributions": [ 44 | "code", 45 | "translation", 46 | "doc", 47 | "ideas", 48 | "review" 49 | ] 50 | }, 51 | { 52 | "login": "Container-00", 53 | "name": "Container", 54 | "avatar_url": "https://avatars.githubusercontent.com/u/61052480?v=4", 55 | "profile": "https://github.com/Container-00", 56 | "contributions": [ 57 | "code", 58 | "translation", 59 | "doc", 60 | "review" 61 | ] 62 | }, 63 | { 64 | "login": "nosalt99", 65 | "name": "nosalt", 66 | "avatar_url": "https://avatars.githubusercontent.com/u/22558493?v=4", 67 | "profile": "https://github.com/nosalt99", 68 | "contributions": [ 69 | "code", 70 | "translation", 71 | "doc", 72 | "infra" 73 | ] 74 | }, 75 | { 76 | "login": "stephenreborn", 77 | "name": "stephenreborn", 78 | "avatar_url": "https://avatars.githubusercontent.com/u/6388610?v=4", 79 | "profile": "https://github.com/stephenreborn", 80 | "contributions": [ 81 | "code", 82 | "translation", 83 | "talk" 84 | ] 85 | }, 86 | { 87 | "login": "666thi", 88 | "name": "666thi", 89 | "avatar_url": "https://avatars.githubusercontent.com/u/109965699?v=4", 90 | "profile": "https://github.com/666thi", 91 | "contributions": [ 92 | "code", 93 | "translation", 94 | "talk" 95 | ] 96 | }, 97 | { 98 | "login": "MagicGordon", 99 | "name": "MagicGordon", 100 | "avatar_url": "https://avatars.githubusercontent.com/u/19465870?v=4", 101 | "profile": "https://github.com/MagicGordon", 102 | "contributions": [ 103 | "code", 104 | "translation", 105 | "talk" 106 | ] 107 | }, 108 | { 109 | "login": "xixifusi1984", 110 | "name": "xixifusi1984", 111 | "avatar_url": "https://avatars.githubusercontent.com/u/39210551?v=4", 112 | "profile": "https://github.com/xixifusi1984", 113 | "contributions": [ 114 | "code", 115 | "translation", 116 | "talk" 117 | ] 118 | }, 119 | { 120 | "login": "yvvw", 121 | "name": "yvvw", 122 | "avatar_url": "https://avatars.githubusercontent.com/u/15168529?v=4", 123 | "profile": "https://github.com/yvvw", 124 | "contributions": [ 125 | "code", 126 | "translation", 127 | "talk" 128 | ] 129 | }, 130 | { 131 | "login": "xiaochuan891102", 132 | "name": "xiaochuan891102", 133 | "avatar_url": "https://avatars.githubusercontent.com/u/109952533?v=4", 134 | "profile": "https://github.com/xiaochuan891102", 135 | "contributions": [ 136 | "code", 137 | "translation", 138 | "talk" 139 | ] 140 | }, 141 | { 142 | "login": "stephenLee", 143 | "name": "stephenLee", 144 | "avatar_url": "https://avatars.githubusercontent.com/u/1144508?v=4", 145 | "profile": "https://github.com/stephenLee", 146 | "contributions": [ 147 | "code", 148 | "translation", 149 | "talk" 150 | ] 151 | } 152 | ], 153 | "contributorsPerLine": 6 154 | } 155 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | - name: Install mdbook 15 | run: | 16 | mkdir mdbook 17 | curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook 18 | echo `pwd`/mdbook >> $GITHUB_PATH 19 | - name: Deploy GitHub Pages 20 | run: | 21 | # This assumes your book is in the root of your repository. 22 | # Just add a `cd` here if you need to change to another directory. 23 | mdbook build 24 | git worktree add gh-pages 25 | git config user.name "Deploy from CI" 26 | git config user.email "" 27 | cd gh-pages 28 | # Delete the ref to avoid keeping history. 29 | git update-ref -d refs/heads/gh-pages 30 | rm -rf * 31 | mv ../book/* . 32 | git add . 33 | git commit -m "Deploy $GITHUB_SHA to gh-pages" 34 | git push --force --set-upstream origin gh-pages -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Move book 中文版](https://move-dao.github.io/move-book-zh/) 2 | 3 | ## [Move book 目录][moveBookDir] 4 | 5 | [各章节译者](Translators.md) 6 | 7 | [moveBookDir]: https://github.com/move-dao/move-book-zh/blob/main/src/SUMMARY.md 8 | 9 | 10 | [![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors-) 11 | 12 | ![GitHub last commit](https://img.shields.io/github/last-commit/move-dao/move-book-zh?color=gold) 13 | 14 | 15 | > Chinese translation of [The Move Language][github-en] 16 | 17 | [github-en]: https://github.com/move-language/move 18 | 19 | ## 依赖 20 | 21 | 构建本书需要 [mdBook],执行以下命令安装: 22 | 23 | [mdBook]: https://github.com/rust-lang-nursery/mdBook 24 | 25 | ```bash 26 | $ cargo install --git https://github.com/rust-lang/mdBook.git mdbook 27 | ``` 28 | 29 | ## 构建 30 | 31 | 构建此书,请输入: 32 | 33 | ``` 34 | $ mdbook build 35 | ``` 36 | 37 | 输出内容存放在 `book` 子目录中。可使用浏览器打开来查看内容。 38 | 39 | ## 预览 40 | 41 | 构建后,输入以下命令即可在本地预览此书,默认情况下在`localhost:3000`: 42 | 43 | ``` 44 | $ mdbook serve 45 | ``` 46 | 47 | `serve` command watches the book's `src` directory for changes, rebuilding the book and refreshing clients for each change; this includes re-creating deleted files still mentioned in `SUMMARY.md`! A websocket connection is used to trigger the client-side refresh. 48 | 49 | 50 | 51 | ## Contributors ✨ 52 | 53 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |

zhang

💻 📖 🌍 🚇

ruyisu

💻 🌍 📖 🚇

lshoo

💻 🌍 📖 🤔 👀

Container

💻 🌍 📖 👀

nosalt

💻 🌍 📖 🚇

stephenreborn

💻 🌍 📢

666thi

💻 🌍 📢

MagicGordon

💻 🌍 📢

xixifusi1984

💻 🌍 📢

yvvw

💻 🌍 📢

xiaochuan891102

💻 🌍 📢

stephenLee

💻 🌍 📢
76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Translators.md: -------------------------------------------------------------------------------- 1 | | | 章节 | 译者 | 校对 | 2 | | -- | -------------------------- | ---------------------------------------------- | ------------------------------------ | 3 | | 0 | Intoduction | Tom | [@lshoo](https://github.com/lshoo) | 4 | | 1 | Modules and Scripts | [@Kusou1](https://github.com/kusou1) | [@lshoo](https://github.com/lshoo) | 5 | | 2 | Move Tutorial | | [@lshoo](https://github.com/lshoo) | 6 | | 3 | Integers | Tom | [@lshoo](https://github.com/lshoo) | 7 | | 4 | Bool | Tom | [@lshoo](https://github.com/lshoo) | 8 | | 5 | Address | ([@stephenLee](https://github.com/stephenLee)) | [@lshoo](https://github.com/lshoo) | 9 | | 6 | Vector | ([@stephenLee](https://github.com/stephenLee)) | [@lshoo](https://github.com/lshoo) | 10 | | 7 | Signer | ([@stephenLee](https://github.com/stephenLee)) | [@lshoo](https://github.com/lshoo) | 11 | | 8 | References | container | [@lshoo](https://github.com/lshoo) | 12 | | 9 | Tuples and Unit | container | [@lshoo](https://github.com/lshoo) | 13 | | 10 | Local Variables and Scopes | @ruyisu | [@lshoo](https://github.com/lshoo) | 14 | | 11 | Equality | @ruyisu | [@lshoo](https://github.com/lshoo) | 15 | | 12 | Abort and Assert | @ruyisu | [@lshoo](https://github.com/lshoo) | 16 | | 13 | Conditionals | [@Kusou1](https://github.com/kusou1) | [@lshoo](https://github.com/lshoo) | 17 | | 14 | While and Loop | [@Kusou1](https://github.com/kusou1) | [@lshoo](https://github.com/lshoo) | 18 | | 15 | Functions | @nosalt99 | [@lshoo](https://github.com/lshoo) | 19 | | 16 | Structs and Resource | @nosalt99 | [@lshoo](https://github.com/lshoo) | 20 | | 17 | Constants | @nosalt99 | [@lshoo](https://github.com/lshoo) | 21 | | 18 | Generics | 小川 | [@lshoo](https://github.com/lshoo) | 22 | | 19 | Type Abilities | 小川 | [@lshoo](https://github.com/lshoo) | 23 | | 20 | Uses and Aliases | 小川 | [@ruyisu](https://github.com/ruy1su) | 24 | | 21 | Friends | @xiaochuan891102 | [@ruyisu](https://github.com/ruy1su) | 25 | | 22 | Packages | @xiaochuan891102 | [@ruyisu](https://github.com/ruy1su) | 26 | | 23 | Unit Test | [@yvvw](https://github.com/yvvw) | [@ruyisu](https://github.com/ruy1su) | 27 | | 24 | Global Storage Structure | [@yvvw](https://github.com/yvvw) | [@ruyisu](https://github.com/ruy1su) | 28 | | 25 | Global Storage Operators | [@yvvw](https://github.com/yvvw) | [@ruyisu](https://github.com/ruy1su) | 29 | | 26 | Standard Library | @MagicGordon | [@ruyisu](https://github.com/ruy1su) | 30 | | 27 | Coding Conventions | @MagicGordon | [@ruyisu](https://github.com/ruy1su) | 31 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Move Book 中文版" 3 | authors = ["The Move China Contributors"] 4 | language = "zh" 5 | multilingual = false 6 | src = "src" 7 | 8 | [output.html] 9 | git-repository-url = "https://github.com/move-dao/move-book-zh" 10 | git-repository-icon = "fa-github" -------------------------------------------------------------------------------- /depoly.sh: -------------------------------------------------------------------------------- 1 | ## this script deploys the static website of move-book-cn to github pages 2 | 3 | ## build static website for book 4 | mdbook build 5 | 6 | ## init git repo 7 | cd book 8 | git init 9 | git config user.name "move-dao" 10 | git config user.email "move-dao@gmail.com" 11 | git add . 12 | git commit -m 'deploy' 13 | git branch -M gh-pages 14 | git remote add origin https://github.com/move-dao/move-book-zh.git 15 | 16 | ## push to github pages 17 | git push -u -f origin gh-pages -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "all-contributors-cli": "^6.20.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /scripts/fix-name.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs') 4 | 5 | async function main() { 6 | const basePath = 'src' 7 | const dirs = fs.readdirSync(`${basePath}`) 8 | const refs = dirs.reduce((acc, dir) => { 9 | const res = dir.match(/^chapter_\d+_(.*)$/) 10 | if (res === null) return acc 11 | acc.push(res) 12 | return acc 13 | }, []) 14 | for (const dir of dirs) { 15 | const filePath = `${basePath}/${dir}` 16 | if (fs.lstatSync(filePath).isDirectory()) continue 17 | 18 | for (const ref of refs) { 19 | fs.rename(`${basePath}/${ref[0]}`, `${basePath}/${ref[1]}`, err => { 20 | throw err 21 | }) 22 | } 23 | } 24 | console.log('fix name done.') 25 | } 26 | 27 | main().catch(err => { 28 | console.error(err) 29 | process.exit(1) 30 | }) -------------------------------------------------------------------------------- /scripts/fix-reference.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs') 4 | 5 | async function main() { 6 | const basePath = 'src' 7 | const dirs = fs.readdirSync(`${basePath}`) 8 | const refs = dirs.reduce((acc, dir) => { 9 | const res = dir.match(/^chapter_\d+_(.*)$/) 10 | if (res === null) return acc 11 | acc.push(res) 12 | return acc 13 | }, []) 14 | for (const dir of dirs) { 15 | const filePath = `${basePath}/${dir}` 16 | if (fs.lstatSync(filePath).isDirectory()) continue 17 | const fileOld = fs.readFileSync(filePath).toString() 18 | let fileNew = fileOld 19 | for (const ref of refs) { 20 | fileNew = fileNew.replaceAll(`./${ref[0]}`, `./${ref[1]}`) 21 | } 22 | if (fileNew !== fileOld) { 23 | fs.writeFileSync(filePath, fileNew) 24 | } 25 | } 26 | console.log('fix reference done.') 27 | } 28 | 29 | main().catch(err => { 30 | console.error(err) 31 | process.exit(1) 32 | }) 33 | -------------------------------------------------------------------------------- /scripts/fix-summary.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs') 4 | 5 | async function main() { 6 | const basePath = 'src' 7 | let newSummary = fs.readFileSync(`./${basePath}/SUMMARY.md`).toString().replace(/chapter_\d+_/g,"") 8 | fs.writeFileSync(`${basePath}/SUMMARY.md`, newSummary) 9 | console.log('fix summary done.') 10 | } 11 | 12 | main().catch(err => { 13 | console.error(err) 14 | process.exit(1) 15 | }) -------------------------------------------------------------------------------- /src/MOVE语言术语中英文对照表.md: -------------------------------------------------------------------------------- 1 | Move 语言术语中英文对照表由 Move-Dao 中文翻译项目组提供,致力于实现 Move 的文档和书籍中文的术语都保持一致性,本表内容将持续更新维护。所有的对 Move 翻译的学生、开发者或编者都应该参照本表的有关术语。 2 | 3 | | English 英文 | Chinese 中文 | Note 备注 | 4 | | ------ | --------- | --------------------------------------------------------------------------- | 5 | | A | | | 6 | | argument | 参数,实参,实际参数 | 不严格区分的话, argument(参数)和 parameter(参量)可以互换地使用 | 7 | | ability | 能力 | | 8 | | annotate | 标注,注解 | | 9 | | B | | 10 | | backing out | 撤销,回滚 | | 11 | | behavior | 特性,行为 | | 12 | | C | | 13 | | copy | 副本 | | 14 | | D | | 15 | | declare, declaration | 声明 | | 16 | | destroy | 销毁 | explicitly destroyed 我翻译为显式销毁 | 17 | | drop | 掉落 | | 18 | | F | | 19 | | field | 字段 | | 20 | | M | | 21 | | mutate | 变更 | | 22 | | mutation | 变更 | | 23 | | S | | 24 | | scope | 作用域 | | 25 | | statement | 语句 | | 26 | | T | | 27 | | type | 类型 | | 28 | | V | | | 29 | | variable | 变量 | | 30 | | variable binding | 变量绑定 | | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # The Move Programming Language 2 | 3 | [引言 (Introduction)](introduction.md) 4 | 5 | ## Getting Started 6 | 7 | - [模块和脚本 (Modules and Scripts)](modules-and-scripts.md) 8 | - [Move 教程 (Move Tutorial)](move-tutorial.md) 9 | 10 | ## Primitive Types 11 | 12 | - [整数 (Integers)](integers.md) 13 | - [布尔类型 (Bool) ](bool.md) 14 | - [地址 (Address)](address.md) 15 | - [向量 (Vector)](vector.md) 16 | - [签名 (Signer)](signer.md) 17 | - [引用 (References)](references.md) 18 | - [元组和 Unit (Tuples and Unit)](tuples.md) 19 | 20 | ## Basic Concepts 21 | 22 | - [局部变量和作用域 (Local Variables and Scopes)](variables.md) 23 | - [等式 (Equality)](equality.md) 24 | - [中止和断言 (Abort and Assert)](abort-and-assert.md) 25 | - [条件语句 (Conditionals)](conditionals.md) 26 | - [循环 (While and Loop)](loops.md) 27 | - [函数 (Functions)](functions.md) 28 | - [结构体和资源 (Structs and Resources)](structs-and-resources.md) 29 | - [常量 (Constants)](constants.md) 30 | - [泛型 (Generics)](generics.md) 31 | - [类型能力 (Type Abilities)](abilities.md) 32 | - [导入和别名 (Uses and Aliases)](uses.md) 33 | - [友元函数 (Friends)](friends.md) 34 | - [程序包 (Packages)](packages.md) 35 | - [单元测试 (Unit Tests)](unit-testing.md) 36 | 37 | ## Global Storage 38 | 39 | - [全局存储结构 (Global Storage Structure)](global-storage-structure.md) 40 | - [全局存储操作 (Global Storage Operators)](global-storage-operators.md) 41 | 42 | ## Reference 43 | 44 | - [标准库 (Standard Library)](standard-library.md) 45 | - [Move 编码规范 (Coding Conventions)](coding-conventions.md) 46 | -------------------------------------------------------------------------------- /src/abilities.md: -------------------------------------------------------------------------------- 1 | # 能力 (abilities) 2 | 3 | Abilities are a typing feature in Move that control what actions are permissible for values of a given type. This system grants fine grained control over the "linear" typing behavior of values, as well as if and how values are used in global storage. This is implemented by gating access to certain bytecode instructions so that for a value to be used with the bytecode instruction, it must have the ability required (if one is required at all—not every instruction is gated by an ability). 4 | 5 | 能力是 Move 语言中的一种类型特性,用于控制对给定类型的值允许哪些操作。 该系统对值的“线性”类型行为以及值如何在全局存储中使用提供细粒度控制。这是通过对某些字节码指令的进行访问控制来实现的,因此对于要与字节码指令一起使用的值,它必须具有所需的能力(如果需要的话,并非每条指令都由能力控制) 6 | 7 | ## 四种能力 (The Four Abilities) 8 | 9 | The four abilities are: 10 | 11 | * [`copy`](#copy) 12 | * Allows values of types with this ability to be copied. 13 | * [`drop`](#drop) 14 | * Allows values of types with this ability to be popped/dropped. 15 | * [`store`](#store) 16 | * Allows values of types with this ability to exist inside a struct in global storage. 17 | * [`key`](#key) 18 | * Allows the type to serve as a key for global storage operations. 19 | 20 | 这四种能力分别是: 21 | 22 | * [`copy`](#copy) 复制 23 | * 允许此类型的值被复制 24 | 25 | * [`drop`](#drop) 丢弃 26 | * 允许此类型的值被弹出/丢弃 27 | 28 | * [`store`](#store) 存储 29 | * 允许此类型的值存在于全局存储的某个结构体中 30 | 31 | * [`key`](#key) 键值 32 | * 允许此类型作为全局存储中的键(具有 `key` 能力的类型才能保存到全局存储中) 33 | 34 | 35 | ### `copy` 36 | 37 | The `copy` ability allows values of types with that ability to be copied. It gates the ability to copy values out of local variables with the [`copy`](./variables.md#move-and-copy) operator and to copy values via references with [dereference `*e`](./references.md#reading-and-writing-through-references). 38 | 39 | If a value has `copy`, all values contained inside of that value have `copy`. 40 | 41 | `copy` 能力允许具有此能力的类型的值被复制。 它限制了从本地变量通过 [`copy`](./variables.md#.move-and-copy)能力复制值以及通过 [`dereference *e`](./references.html#reading-and-writing-through-references)复制值这两种情况之外的复制操作。 42 | 43 | 如果一个值具有 `copy` 能力,那么这个值内部的所有值都有 `copy` 能力。 44 | 45 | ### `drop` 46 | 47 | The `drop` ability allows values of types with that ability to be dropped. By dropped, we mean that value is not transferred and is effectively destroyed as the Move program executes. As such, this ability gates the ability to ignore values in a multitude of locations, including: 48 | * not using the value in a local variable or parameter 49 | * not using the value in a [sequence via `;`](./variables.md#expression-blocks) 50 | * overwriting values in variables in [assignments](./variables.md#assignments) 51 | * overwriting values via references when [writing `*e1 = e2`](./references.md#reading-and-writing-through-references). 52 | 53 | If a value has `drop`, all values contained inside of that value have `drop`. 54 | 55 | `drop` 能力允许类型的值被丢弃。丢弃的意思程序执行后值会被有效的销毁而不必被转移。因此,这个能力限制在多个位置忽略使用值的可能性,包括: 56 | * 未被使用的局部变量或者参数 57 | * 未被使用的 [`sequence` via `;`](./variables.md#expression-blocks)中的值 58 | * 覆盖[赋值(assignments)](./variables.html#assignments)变量中的值 59 | * [写入(writing) `*e1 = e2`](https://move-language.github.io/move/references.html#reading-and-writing-through-references) 时通过引用覆盖的值。 60 | 61 | 如果一个值具有 `drop` 能力,那么这个值内部的所有值都有 `drop` 能力。 62 | 63 | ### `store` 64 | 65 | The `store` ability allows values of types with this ability to exist inside of a struct (resource) in global storage, *but* not necessarily as a top-level resource in global storage. This is the only ability that does not directly gate an operation. Instead it gates the existence in global storage when used in tandem with `key`. 66 | 67 | If a value has `store`, all values contained inside of that value have `store` 68 | 69 | `store` 能力允许具有这种能力的类型的值位于[全局存储](./global-storage-operators.html)中的结构体(资源)内, *但不一定是* 全局存储中的顶级资源。这是唯一不直接限制操作的能力。相反,当(`store`)与 `key` 一起使用时,它对全局存储中的可行性进行把关。。 70 | 71 | 如果一个值具有 `store` 能力,那么这个值内部的所有值都有 `store` 能力。 72 | 73 | ### `key` 74 | 75 | The `key` ability allows the type to serve as a key for [global storage operations](./global-storage-operators.md). It gates all global storage operations, so in order for a type to be used with `move_to`, `borrow_global`, `move_from`, etc., the type must have the `key` ability. Note that the operations still must be used in the module where the `key` type is defined (in a sense, the operations are private to the defining module). 76 | 77 | If a value has `key`, all values contained inside of that value have `store`. This is the only ability with this sort of asymmetry. 78 | 79 | `key` 能力允许此类型作为[全局存储](./global-storage-operators.html)中的键。它会限制所有[全局存储](./global-storage-operators.html)中的操作,因此一个类型如果与 `move_to`, `borrow_global`, `move_from` 等一起使用,那么这个类型必须具备 `key` 能力。请注意,这些操作仍然必须在定义 `key` 类型的模块中使用(从某种意义上说,这些操作是此模块的私有操作)。 80 | 81 | 如果有一个值有 `key` 能力,那么这个值包含的所有字段值也都具有 `store` 能力,`key` 能力是唯一一个具有非对称的能力。 82 | 83 | ## Builtin Types (内置类型) 84 | 85 | 86 | Most primitive, builtin types have `copy`, `drop`, and `store` with the exception of `signer`, which just has `store` 87 | 88 | * `bool`, `u8`, `u64`, `u128`, and `address` all have `copy`, `drop`, and `store`. 89 | * `signer` has `drop` 90 | * Cannot be copied and cannot be put into global storage 91 | * `vector` may have `copy`, `drop`, and `store` depending on the abilities of `T`. 92 | * See [Conditional Abilities and Generic Types](#conditional-abilities-and-generic-types) for more details. 93 | * Immutable references `&` and mutable references `&mut` both have `copy` and `drop`. 94 | * This refers to copying and dropping the reference itself, not what they refer to. 95 | * References cannot appear in global storage, hence they do not have `store`. 96 | 97 | None of the primitive types have `key`, meaning none of them can be used directly with the [global storage operations](./global-storage-operators.md). 98 | 99 | 几乎所有内置的基本类型具都有 `copy`,`drop`,以及 `store` 能力,`singer` 除外,它只有 `drop` 能力(原文是 `store` 有误,译者注) 100 | 101 | * `bool`, `u8`, `u64`, `u128`, `address` 都具有 `copy`, `drop`, 以及 `store` 能力。 102 | * `signer` 具有 `drop` 能力。 不能被复制以及不能被存放在全局存储中 103 | * `vector` 可能具有 `copy`,`drop`,以及`store` 能力,这依赖于 `T` 具有的能力。 查看 [条件能力与泛型类型](#conditional-abilities-and-generic-types)获取详情 104 | * 不可变引用 `&` 和可变引用 `&mut` 都具有 `copy` 和 `drop` 能力。 105 | * 这是指复制和删除引用本身,而不是它们所引用的内容。 106 | * 引用不能出现在全局存储中,因此它们没有 `store` 能力。 107 | 108 | 所有基本类型都没有 `key`,这意味着它们都不能直接用于[全局存储操作](./global-storage-operators.html)。 109 | 110 | ## Annotating Structs (标注结构体) 111 | 112 | To declare that a `struct` has an ability, it is declared with `has ` after the struct name but before the fields. For example: 113 | 114 | 要声明一个 `struct` 具有某个能力,它在结构体名称之后, 在字段之前用 `has ` 声明。例如: 115 | 116 | ```move 117 | struct Ignorable has drop { f: u64 } 118 | struct Pair has copy, drop, store { x: u64, y: u64 } 119 | ``` 120 | 121 | In this case: `Ignorable` has the `drop` ability. `Pair` has `copy`, `drop`, and `store`. 122 | 123 | 在这个例子中:`Ignorable` 具有 `drop` 能力。 `Pair` 具有 `copy`、`drop` 和 `store` 能力。 124 | 125 | All of these abilities have strong guarantees over these gated operations. The operation can be performed on the value only if it has that ability; even if the value is deeply nested inside of some other collection! 126 | 127 | 所有这些能力对这些访问操作都有强有力的保证。只有具有该能力,才能对值执行对应的操作;即使该值深层嵌套在其他集合中! 128 | 129 | As such: when declaring a struct’s abilities, certain requirements are placed on the fields. All fields must satisfy these constraints. These rules are necessary so that structs satisfy the reachability rules for the abilities given above. If a struct is declared with the ability... 130 | 131 | * `copy`, all fields must have `copy`. 132 | * `drop`, all fields must have `drop`. 133 | * `store`, all fields must have `store`. 134 | * `key`, all fields must have `store`. 135 | * `key` is the only ability currently that doesn’t require itself. 136 | 137 | 因此:在声明结构体的能力时,对字段提出了某些要求。所有字段都必须满足这些约束。这些规则是必要的,以便结构体满足上述功能的可达性规则。如果一个结构被声明为具有某能力: 138 | 139 | * `copy`, 所有的字段必须具有 `copy` 能力。 140 | * `drop`,所有的字段必须具有 `drop` 能力。 141 | * `store`,所有的字段必须具有 `store` 能力。 142 | * `key`,所有的字段必须具有 `store` 能力。`key` 是目前唯一不需要包含自身的能力。 143 | 144 | 例如: 145 | 146 | ```move 147 | // A struct without any abilities 148 | struct NoAbilities {} 149 | 150 | struct WantsCopy has copy { 151 | f: NoAbilities, // ERROR 'NoAbilities' does not have 'copy' 152 | } 153 | ``` 154 | 155 | and similarly: 156 | 157 | 类似的: 158 | 159 | ```move 160 | // A struct without any abilities 161 | struct NoAbilities {} 162 | 163 | struct MyResource has key { 164 | f: NoAbilities, // Error 'NoAbilities' does not have 'store' 165 | } 166 | ``` 167 | 168 | ## Conditional Abilities and Generic Types (条件能力与泛型类型) 169 | 170 | When abilities are annotated on a generic type, not all instances of that type are guaranteed to have that ability. Consider this struct declaration: 171 | 172 | 在泛型类型上标注能力时,并非该类型的所有实例都保证具有该能力。考虑这个结构体声明: 173 | 174 | ```move 175 | struct Cup has copy, drop, store, key { item: T } 176 | ``` 177 | 178 | 179 | It might be very helpful if `Cup` could hold any type, regardless of its abilities. The type system can *see* the type parameter, so it should be able to remove abilities from `Cup` if it *sees* a type parameter that would violate the guarantees for that ability. 180 | 181 | This behavior might sound a bit confusing at first, but it might be more understandable if we think about collection types. We could consider the builtin type `vector` to have the following type declaration: 182 | 183 | 如果 `Cup` 可以容纳任何类型,可能会很有帮助,不管它的能力如何。类型系统可以 *看到* 类型参数,因此,如果它 *发现* 一个类型参数违反了对该能力的保证,它应该能够从 `Cup` 中删除能力。 184 | 185 | 这种行为一开始可能听起来有点令人困惑,但如果我们考虑一下集合类型,它可能会更容易理解。我们可以认为内置类型 `Vector` 具有以下类型声明: 186 | 187 | ```move 188 | vector has copy, drop, store; 189 | ``` 190 | 191 | We want `vector`s to work with any type. We don't want separate `vector` types for different abilities. So what are the rules we would want? Precisely the same that we would want with the field rules above. So, it would be safe to copy a `vector` value only if the inner elements can be copied. It would be safe to ignore a `vector` value only if the inner elements can be ignored/dropped. And, it would be safe to put a `vector` in global storage only if the inner elements can be in global storage. 192 | 193 | To have this extra expressiveness, a type might not have all the abilities it was declared with depending on the instantiation of that type; instead, the abilities a type will have depends on both its declaration **and** its type arguments. For any type, type parameters are pessimistically assumed to be used inside of the struct, so the abilities are only granted if the type parameters meet the requirements described above for fields. Taking `Cup` from above as an example: 194 | 195 | * `Cup` has the ability `copy` only if `T` has `copy`. 196 | * It has `drop` only if `T` has `drop`. 197 | * It has `store` only if `T` has `store`. 198 | * It has `key` only if `T` has `store`. 199 | 200 | 我们希望 `vector` 适用于任何类型。我们不希望针对不同的能力使用不同的 `vector` 类型。那么我们想要的规则是什么?与上面的字段规则完全相同。因此,仅当可以复制内部元素时,复制`vector` 值才是安全的。仅当可以忽略/丢弃内部元素时,忽略 `vector` 值才是安全的。而且,仅当内部元素可以在全局存储中时,将向量放入全局存储中才是安全的。 201 | 202 | 为了具有这种额外的表现力,一个类型可能不具备它声明的所有能力,具体取决于该类型的实例化;相反,一个类型的能力取决于它的声明 **和** 它的类型参数。对于任何类型,类型参数都被悲观地假定为在结构体内部使用,因此只有在类型参数满足上述字段要求时才授予这些能力。以上面的 `Cup` 为例: 203 | 204 | * `Cup` 拥有 `copy` 能力 仅当 `T` 拥有 `copy` 能力时。 205 | * `Cup` 拥有 `drop` 能力 仅当 `T` 拥有 `drop` 能力时。 206 | * `Cup` 拥有 `store` 能力 仅当 `T` 拥有 `store` 能力时。 207 | * `Cup` 拥有 `key` 能力 仅当 `T` 拥有 `store` 能力时。 208 | 209 | Here are examples for this conditional system for each ability: 210 | 211 | 以下是每个能力的条件系统的示例: 212 | 213 | ### Example: conditional `copy` 214 | 215 | ```move 216 | struct NoAbilities {} 217 | struct S has copy, drop { f: bool } 218 | struct Cup has copy, drop, store { item: T } 219 | 220 | fun example(c_x: Cup, c_s: Cup) { 221 | // Valid, 'Cup' has 'copy' because 'u64' has 'copy' 222 | let c_x2 = copy c_x; 223 | // Valid, 'Cup' has 'copy' because 'S' has 'copy' 224 | let c_s2 = copy c_s; 225 | } 226 | 227 | fun invalid(c_account: Cup, c_n: Cup) { 228 | // Invalid, 'Cup' does not have 'copy'. 229 | // Even though 'Cup' was declared with copy, the instance does not have 'copy' 230 | // because 'signer' does not have 'copy' 231 | let c_account2 = copy c_account; 232 | // Invalid, 'Cup' does not have 'copy' 233 | // because 'NoAbilities' does not have 'copy' 234 | let c_n2 = copy c_n; 235 | } 236 | ``` 237 | 238 | ### Example: conditional `drop` 239 | 240 | ```move 241 | struct NoAbilities {} 242 | struct S has copy, drop { f: bool } 243 | struct Cup has copy, drop, store { item: T } 244 | 245 | fun unused() { 246 | Cup { item: true }; // Valid, 'Cup' has 'drop' 247 | Cup { item: S { f: false }}; // Valid, 'Cup' has 'drop' 248 | } 249 | 250 | fun left_in_local(c_account: Cup): u64 { 251 | let c_b = Cup { item: true }; 252 | let c_s = Cup { item: S { f: false }}; 253 | // Valid return: 'c_account', 'c_b', and 'c_s' have values 254 | // but 'Cup', 'Cup', and 'Cup' have 'drop' 255 | 0 256 | } 257 | 258 | fun invalid_unused() { 259 | // Invalid, Cannot ignore 'Cup' because it does not have 'drop'. 260 | // Even though 'Cup' was declared with 'drop', the instance does not have 'drop' 261 | // because 'NoAbilities' does not have 'drop' 262 | Cup { item: NoAbilities {}}; 263 | } 264 | 265 | fun invalid_left_in_local(): u64 { 266 | let n = Cup { item: NoAbilities {}}; 267 | // Invalid return: 'c_n' has a value 268 | // and 'Cup' does not have 'drop' 269 | 0 270 | } 271 | ``` 272 | 273 | ### Example: conditional `store` 274 | 275 | ```move 276 | struct Cup has copy, drop, store { item: T } 277 | 278 | // 'MyInnerResource' is declared with 'store' so all fields need 'store' 279 | struct MyInnerResource has store { 280 | yes: Cup, // Valid, 'Cup' has 'store' 281 | // no: Cup, Invalid, 'Cup' does not have 'store' 282 | } 283 | 284 | // 'MyResource' is declared with 'key' so all fields need 'store' 285 | struct MyResource has key { 286 | yes: Cup, // Valid, 'Cup' has 'store' 287 | inner: Cup, // Valid, 'Cup' has 'store' 288 | // no: Cup, Invalid, 'Cup' does not have 'store' 289 | } 290 | ``` 291 | 292 | ### Example: conditional `key` 293 | 294 | ```move 295 | struct NoAbilities {} 296 | struct MyResource has key { f: T } 297 | 298 | fun valid(account: &signer) acquires MyResource { 299 | let addr = signer::address_of(account); 300 | // Valid, 'MyResource' has 'key' 301 | let has_resource = exists>(addr); 302 | if (!has_resource) { 303 | // Valid, 'MyResource' has 'key' 304 | move_to(account, MyResource { f: 0 }) 305 | }; 306 | // Valid, 'MyResource' has 'key' 307 | let r = borrow_global_mut>(addr) 308 | r.f = r.f + 1; 309 | } 310 | 311 | fun invalid(account: &signer) { 312 | // Invalid, 'MyResource' does not have 'key' 313 | let has_it = exists>(addr); 314 | // Invalid, 'MyResource' does not have 'key' 315 | let NoAbilities {} = move_from(addr); 316 | // Invalid, 'MyResource' does not have 'key' 317 | move_to(account, NoAbilities {}); 318 | // Invalid, 'MyResource' does not have 'key' 319 | borrow_global(addr); 320 | } 321 | ``` 322 | -------------------------------------------------------------------------------- /src/abort-and-assert.md: -------------------------------------------------------------------------------- 1 | # 中止和断言 (Abort and Assert) 2 | 3 | [`return`](./functions.md) and `abort` are two control flow constructs that end execution, one for 4 | the current function and one for the entire transaction. 5 | 6 | More information on [`return` can be found in the linked section](./functions.md) 7 | 8 | [`return`](./functions.md) 和 `abort` 是两种结束程序执行的控制流结构。前者针对当前函数,后者针对整个事务。 9 | 10 | [`return`](./functions.md)的更多信息可以参考链接中的文章。 11 | 12 | ## `abort` 中止 13 | 14 | `abort` is an expression that takes one argument: an **abort code** of type `u64`. For example: 15 | 16 | `abort` 表达式只接受一个参数: 类型为 `u64` 的**中止代码**。例如: 17 | 18 | ```move 19 | abort 42 20 | ``` 21 | 22 | The `abort` expression halts execution the current function and reverts all changes made to global 23 | state by the current transaction. There is no mechanism for "catching" or otherwise handling an `abort`. 24 | 25 | `abort` 表达式会中止执行当前函数并恢复当前事务对全局状态所做的所有更改。Move语言没有“捉捕”或者额外处理`abort`的机制。 26 | 27 | Luckily, in Move transactions are all or nothing, meaning any changes to global storage are made all 28 | at once only if the transaction succeeds. Because of this transactional commitment of changes, after 29 | an abort there is no need to worry about backing out changes. While this approach is lacking in 30 | flexibility, it is incredibly simple and predictable. 31 | 32 | 幸运的是,在Move里事务的计算要么完全执行要么完全不执行。这意味着只有在事务成功时,任何对全局存储状态的改变才会被一并执行。 33 | 由于这种对于所有更改的事务承诺,在 `abort` 之后我们不需要担心去回滚任何更改。尽管这种方法缺少灵活性,它还是非常简单和可预测的。 34 | 35 | 36 | Similar to [`return`](./functions.md), `abort` is useful for exiting control flow when some condition cannot be met. 37 | 38 | In this example, the function will pop two items off of the vector, but will abort early if the vector does not have two items 39 | 40 | 与 [`return`](./functions.md)相似, 在一些条件无法被满足的时候,`abort` 可以被用于退出控制流(control flow)。 41 | 42 | 在以下示例中,目标函数会从vector里弹出两个元素,但是如果vector中并没有两个元素,函数会提前中止。 43 | 44 | ```move= 45 | use std::vector; 46 | fun pop_twice(v: &mut vector): (T, T) { 47 | if (vector::length(v) < 2) abort 42; 48 | 49 | (vector::pop_back(v), vector::pop_back(v)) 50 | } 51 | ``` 52 | 53 | This is even more useful deep inside a control-flow construct. For example, this function checks 54 | that all numbers in the vector are less than the specified `bound`. And aborts otherwise 55 | 56 | 这在控制流结构的深处甚至会更有用。例如,此函数检查vector中是否所有数字都小于指定的边界(`bound`)。否则函数中止: 57 | 58 | ```move= 59 | use std::vector; 60 | fun check_vec(v: &vector, bound: u64) { 61 | let i = 0; 62 | let n = vector::length(v); 63 | while (i < n) { 64 | let cur = *vector::borrow(v, i); 65 | if (cur > bound) abort 42; 66 | i = i + 1; 67 | } 68 | } 69 | ``` 70 | 71 | ### `assert` 断言 72 | 73 | `assert` is a builtin, macro-like operation provided by the Move compiler. It takes two arguments, a condition of type `bool` and a code of type `u64` 74 | 75 | `assert` 是 Move 编译器提供的内置的类宏(macro-like)操作。它需要两个参数:一个 `bool` 类型的条件和一个 `u64` 类型的错误状态码(类似HTTP中的StatusCode: 404, 500等,译者注) 76 | 77 | ```move 78 | assert!(condition: bool, code: u64) 79 | ``` 80 | 81 | Since the operation is a macro, it must be invoked with the `!`. This is to convey that the 82 | arguments to `assert` are call-by-expression. In other words, `assert` is not a normal function and 83 | does not exist at the bytecode level. It is replaced inside the compiler with 84 | 85 | 由于该操作是一个宏,因此必须使用 `!` 调用它。这是为了表达 `assert` 的参数属于表达式调用(call-by-expression)。换句话说,`assert` 不是一个正常的函数,并且在字节码(bytecode)级别不存在。它在编译器内部被替换为以下代码: 86 | 87 | ```move 88 | if (condition) () else abort code 89 | ``` 90 | 91 | `assert` is more commonly used than just `abort` by itself. The `abort` examples above can be rewritten using `assert` 92 | 93 | `assert` 比 `abort` 本身更常用。上面的 `abort` 示例可以使用 `assert` 重写 94 | 95 | ```move= 96 | use std::vector; 97 | fun pop_twice(v: &mut vector): (T, T) { 98 | assert!(vector::length(v) >= 2, 42); // 现在使用'assert' 99 | 100 | (vector::pop_back(v), vector::pop_back(v)) 101 | } 102 | ``` 103 | 104 | 和 105 | 106 | ```move= 107 | use std::vector; 108 | fun check_vec(v: &vector, bound: u64) { 109 | let i = 0; 110 | let n = vector::length(v); 111 | while (i < n) { 112 | let cur = *vector::borrow(v, i); 113 | assert!(cur <= bound, 42); // 现在使用 'assert' 114 | i = i + 1; 115 | } 116 | } 117 | ``` 118 | 119 | Note that because the operation is replaced with this `if-else`, the argument for the `code` is not 120 | always evaluated. For example: 121 | 122 | 请注意,因为此操作被替换为 `if-else`,这段 `代码` 的参数不是总是被执行(evaluated)。例如: 123 | 124 | ```move 125 | assert!(true, 1 / 0) 126 | ``` 127 | 128 | Will not result in an arithmetic error, it is equivalent to 129 | 130 | 不会导致算术错误,因为它相当于: 131 | 132 | ```move 133 | if (true) () else (1 / 0) 134 | ``` 135 | 136 | So the arithmetic expression is never evaluated! 137 | 138 | 所以这个算术表达式永远不会被执行(evaluated)! 139 | 140 | ### Abort codes in the Move VM (Move虚拟机中的中止代码) 141 | 142 | When using `abort`, it is important to understand how the `u64` code will be used by the VM. 143 | 144 | Normally, after successful execution, the Move VM produces a change-set for the changes made to 145 | global storage (added/removed resources, updates to existing resources, etc). 146 | 147 | 当使用 `abort` 时,理解虚拟机将如何使用 `u64` 代码是非常重要的。 148 | 149 | 通常,在成功执行后,Move 虚拟机会为对全局存储(添加/删除资源、更新现有资源等)所做的更改生成一个更改集。 150 | 151 | If an `abort` is reached, the VM will instead indicate an error. Included in that error will be two 152 | pieces of information: 153 | 154 | - The module that produced the abort (address and name) 155 | - The abort code. 156 | 157 | For example 158 | 159 | 如果执行到 `abort` 代码,虚拟机将指示错误。该错误中包含两块信息: 160 | 161 | - 发生中止的模块(地址和名称) 162 | - 错误状态码。 163 | 164 | 例如 165 | 166 | ```move= 167 | address 0x2 { 168 | module example { 169 | public fun aborts() { 170 | abort 42 171 | } 172 | } 173 | } 174 | 175 | script { 176 | fun always_aborts() { 177 | 0x2::example::aborts() 178 | } 179 | } 180 | ``` 181 | 182 | If a transaction, such as the script `always_aborts` above, calls `0x2::example::aborts`, the VM 183 | would produce an error that indicated the module `0x2::example` and the code `42`. 184 | 185 | This can be useful for having multiple aborts being grouped together inside a module. 186 | 187 | In this example, the module has two separate error codes used in multiple functions 188 | 189 | 如果一个事务,例如上面的脚本 `always_aborts` 调用了 `0x2::example::aborts`,虚拟机将产生一个指示模块 `0x2::example` 和错误状态码 `42` 的错误。 190 | 191 | 这在一个模块内将多个中止功能组合起来会很有用。 192 | 193 | 在以下示例中,模块有两个单独的错误状态码,用于多个函数 194 | 195 | ```move= 196 | address 0x42 { 197 | module example { 198 | 199 | use std::vector; 200 | 201 | const EMPTY_VECTOR: u64 = 0; 202 | const INDEX_OUT_OF_BOUNDS: u64 = 1; 203 | 204 | // 移动 i 到 j, 移动 j 到 k, 移动 k 到 i 205 | public fun rotate_three(v: &mut vector, i: u64, j: u64, k: u64) { 206 | let n = vector::length(v); 207 | assert!(n > 0, EMPTY_VECTOR); 208 | assert!(i < n, INDEX_OUT_OF_BOUNDS); 209 | assert!(j < n, INDEX_OUT_OF_BOUNDS); 210 | assert!(k < n, INDEX_OUT_OF_BOUNDS); 211 | 212 | vector::swap(v, i, k); 213 | vector::swap(v, j, k); 214 | } 215 | 216 | public fun remove_twice(v: &mut vector, i: u64, j: u64): (T, T) { 217 | let n = vector::length(v); 218 | assert!(n > 0, EMPTY_VECTOR); 219 | assert!(i < n, INDEX_OUT_OF_BOUNDS); 220 | assert!(j < n, INDEX_OUT_OF_BOUNDS); 221 | assert!(i > j, INDEX_OUT_OF_BOUNDS); 222 | 223 | (vector::remove(v, i), vector::remove(v, j)) 224 | } 225 | } 226 | } 227 | ``` 228 | 229 | ## The type of `abort` (`abort` 的类型) 230 | 231 | The `abort i` expression can have any type! This is because both constructs break from the normal 232 | control flow, so they never need to evaluate to the value of that type. 233 | 234 | The following are not useful, but they will type check 235 | 236 | `abort i` 表达式可以有任何类型!这是因为这两种构造都打破了正常控制流,因此他们永远不需要计算该类型的值。 237 | 238 | 以下的示例不是特别有用,但它们会做类型检查 239 | 240 | ```move 241 | let y: address = abort 0; 242 | ``` 243 | 244 | This behavior can be helpful in situations where you have a branching instruction that produces a 245 | value on some branches, but not all. For example: 246 | 247 | 在您有一个分支指令,并且这个指令会产生某些分支(不是全部)的值的时候,这种行为会非常有用。例如: 248 | 249 | ```move 250 | let b = 251 | if (x == 0) false 252 | else if (x == 1) true 253 | else abort 42; 254 | // ^^^^^^^^ `abort 42` 的类型是 `bool` 255 | ``` 256 | -------------------------------------------------------------------------------- /src/address.md: -------------------------------------------------------------------------------- 1 | # 地址(Address) 2 | 3 | `address` is a built-in type in Move that is used to represent locations (sometimes called accounts) in global storage. An `address` value is a 128-bit (16 byte) identifier. At a given address, two things can be stored: [Modules](./modules-and-scripts.md) and [Resources](./structs-and-resources.md). 4 | 5 | Although an `address` is a 128 bit integer under the hood, Move addresses are intentionally opaque---they cannot be created from integers, they do not support arithmetic operations, and they cannot be modified. Even though there might be interesting programs that would use such a feature (e.g., pointer arithmetic in C fills a similar niche), Move does not allow this dynamic behavior because it has been designed from the ground up to support static verification. 6 | 7 | You can use runtime address values (values of type `address`) to access resources at that address. You *cannot* access modules at runtime via address values. 8 | 9 | `地址(address)`是 Move 中的内置类型,用于表示全局存储中的的位置(有时称为账户)。`地址(address)` 值是一个 128 位(16 字节)标识符。在一个给定的地址,可以存储两样东西:[模块(Module)](./modules-and-scripts.md)和[资源(Resources)](./structs-and-resources.md)。 10 | 11 | 虽然`地址(address)`在底层是一个 128 位整数,但 Move 语言有意让其不透明 —— 它们不能从整数创建,不支持算术运算,也不能修改。即使可能有一些有趣的程序会使用这种特性(例如,C 中的指针算法实现了类似壁龛(niche)的功能),但 Move 语言不允许这种动态行为,因为它从头开始就被设计为支持静态验证。*(壁龛指安装在墙壁上的小格子或在墙身上留出的作为贮藏设施的空间,最早在宗教上是指排放佛像的小空间,现在多用在家庭装修上,因其不占建筑面积,使用比较方便,深受大家喜爱,Joe 注)* 12 | 13 | 你可以通过运行时地址值(`address` 类型的值)来访问该地址处的资源。但*无法*在运行时通过地址值访问模块。 14 | 15 | ## 地址及其语法(Addresses and Their Syntax) 16 | 17 | Addresses come in two flavors, named or numerical. The syntax for a named address follows the 18 | same rules for any named identifier in Move. The syntax of a numerical address is not restricted 19 | to hex-encoded values, and any valid [`u128` numerical value](./integers.md) can be used as an 20 | address value, e.g., `42`, `0xCAFE`, and `2021` are all valid numerical address 21 | literals. 22 | 23 | 地址有两种形式:*命名的*或*数值的*。命名地址的语法遵循 Move 命名标识符的规则。数值地址的语法不受十六进制编码值的限制,任何有效的 [`u128` 数值](./integers.md)都可以用作地址值。例如,`42`,`0xCFAE` 和 `2021` 都是合法有效的数值地址字面量(literal)。 24 | 25 | To distinguish when an address is being used in an expression context or not, the 26 | syntax when using an address differs depending on the context where it's used: 27 | * When an address is used as an expression the address must be prefixed by the `@` character, i.e., [`@`](./integers.md) or `@`. 28 | * Outside of expression contexts, the address may be written without the leading `@` character, i.e., [``](./integers.md) or ``. 29 | 30 | 31 | 为了区分何时在表达式上下文中使用地址,使用地址时的语法根据使用地址的上下文而有所不同: 32 | 33 | * 当地址被用作表达式时,地址必须以 `@` 字符为前缀,例如:[`@`](./integers.md) 或 `@`。 34 | * 在表达式上下文之外,地址可以不带前缀字符 `@`。例如:[``](./integers.md) 或 ``。 35 | 36 | In general, you can think of `@` as an operator that takes an address from being a namespace item to being an expression item. 37 | 38 | 通常,可以将 `@` 视为将地址从命名空间项变为表达式项的运算符。 39 | 40 | ## 命名地址(Named Addresses) 41 | 42 | Named addresses are a feature that allow identifiers to be used in place of 43 | numerical values in any spot where addresses are used, and not just at the 44 | value level. Named addresses are declared and bound as top level elements 45 | (outside of modules and scripts) in Move Packages, or passed as arguments 46 | to the Move compiler. 47 | 48 | 命名地址是一项特性,它允许在使用地址的任何地方使用标识符代替数值,而不仅仅是在值级别。命名地址被声明并绑定为 Move 包中的顶级元素(模块和脚本之外)或作为参数传递给 Move 编译器。 49 | 50 | Named addresses only exist at the source language level and will be fully 51 | substituted for their value at the bytecode level. Because of this, modules 52 | and module members _must_ be accessed through the module's named address 53 | and not through the numerical value assigned to the named address during 54 | compilation, e.g., `use my_addr::foo` is _not_ equivalent to `use 0x2::foo` 55 | even if the Move program is compiled with `my_addr` set to `0x2`. This 56 | distinction is discussed in more detail in the section on [Modules and 57 | Scripts](./modules-and-scripts.md). 58 | 59 | 命名地址仅存在于源语言级别,并将在字节码级别完全替代它们的值。因此,模块和模块成员*必须*通过模块的命名地址而不是编译期间分配给命名地址的数值来访问,例如:`use my_addr::foo` *不等于* `use 0x2::foo`,即使 Move 程序编译时将 `my_addr` 设置成 `0x2`。这个区别在[模块和脚本](./modules-and-scripts.md)一节中有更详细的讨论。 60 | 61 | ### 例子(Examples) 62 | 63 | ```move 64 | let a1: address = @0x1; // 0x00000000000000000000000000000001 的缩写 65 | let a2: address = @0x42; // 0x00000000000000000000000000000042 的缩写 66 | let a3: address = @0xDEADBEEF; // 0x000000000000000000000000DEADBEEF 的缩写 67 | let a4: address = @0x0000000000000000000000000000000A; 68 | let a5: address = @std; // 将命名地址 `std` 的值赋给 `a5` 69 | let a6: address = @66; 70 | let a7: address = @0x42; 71 | 72 | module 66::some_module { // 不在表达式上下文中,所以不需要 @ 73 | use 0x1::other_module; // 不在表达式上下文中,所以不需要 @ 74 | use std::vector; // 使用其他模块时,可以使用命名地址作为命名空间项 75 | ... 76 | } 77 | 78 | module std::other_module { // 可以使用命名地址作为命名空间项来声明模块 79 | ... 80 | } 81 | ``` 82 | 83 | ## 全局存储操作(Global Storage Operations) 84 | 85 | The primary purpose of `address` values are to interact with the global storage operations. 86 | 87 | `address` values are used with the `exists`, `borrow_global`, `borrow_global_mut`, and `move_from` [operations](./global-storage-operators.md). 88 | 89 | The only global storage operation that *does not* use `address` is `move_to`, which uses [`signer`](./signer.md). 90 | 91 | `address` 值主要用来与全局存储操作进行交互。 92 | 93 | `address` 值与 `exists`、`borrow_global`、`borrow_global_mut` 和 `move_from` [操作(operation)](./global-storage-operators.md)一起使用。 94 | 95 | 唯一*不使用* `address` 的全局存储操作是 `move_to`,它使用了 [`signer`](./signer.md)。 96 | 97 | ## 所有权(Ownership) 98 | 99 | As with the other scalar values built-in to the language, `address` values are implicitly copyable, meaning they can be copied without an explicit instruction such as [`copy`](./variables.md#move-and-copy). 100 | 101 | 与 Move 语言内置的其他标量值一样,`address` 值是隐式可复制的,这意味着它们可以在没有显式指令(例如 [`copy`](./variables.md#移动和复制move-and-copy))的情况下复制。 102 | -------------------------------------------------------------------------------- /src/bool.md: -------------------------------------------------------------------------------- 1 | # 布尔类型 (Bool) 2 | 3 | `bool`is Move's primitive type for boolean `true` and `false`values. 4 | 5 | `bool` 是 Move 布尔基本类型,有 `true` 和 `false` 两个值。 6 | 7 | ## 字面量 (Literals) 8 | 9 | Literals for `bool` are either `true` or `false` . 10 | 11 | 布尔类型字面值只能是 `true` 或者 `false`中的一个 。 12 | 13 | ## 操作 (Operations) 14 | 15 | ### 逻辑运算 (Logical) 16 | 17 | `bool`supports three logical operations: 18 | 19 | | Syntax | Description | Equivalent Expression | 20 | | ------------------------- | ---------------------------- | ------------------------------------------------------------------- | 21 | | `&&` | short-circuiting logical and | `p && q` is equivalent to `if (p) q else false` | 22 | | || | short-circuiting logical or | p || q is equivalent to `if (p) true else q` | 23 | | `!` | logical negation | `!p` is equivalent to `if (p) false else true` | 24 | 25 | 26 | `bool` 支持三种逻辑运算: 27 | 28 | | 句法 | 描述 | Equivalent Expression | 29 | | ------ | ---------------------------- | ----------------------------------------------- | 30 | | `&&` | 短路逻辑与(short-circuiting logical and) | `p && q` 等价于 `if (p) q else false` | 31 | | || | 短路逻辑或(short-circuiting logical or) | `p || q` 等价于 `if (p) true else q` | 32 | | `!` | 逻辑非(logical negation) | `!p` 等价于 `if (p) false else true` | 33 | 34 | 35 | ### 控制流 (Control Flow) 36 | 37 | `bool`values are used in several of Move's control-flow constructs: 38 | 39 | 布尔值用于 Move 的多个控制流结构中: 40 | 41 | - [`if (bool) { ... }`](./conditionals.html) 42 | - [`while (bool) { .. }`](/loops.html) 43 | - [`assert!(bool, u64)`](./abort-and-assert.html) 44 | 45 | ## 所有权 (Ownership) 46 | 47 | As with the other scalar values built-in to the language, boolean values are implicitly copyable, meaning they can be copied without an explicit instruction such as `[copy]().` 48 | 49 | 与语言内置的其他标量值一样,布尔值是隐式可复制的,这意味着它们可以在没有明确指令如[`copy`](./variables.md#move-and-copy)的情况下复制。 50 | -------------------------------------------------------------------------------- /src/coding-conventions.md: -------------------------------------------------------------------------------- 1 | # Move 编码规范(Move Coding Conventions) 2 | 3 | This section lays out some basic coding conventions for Move that the Move team has found helpful. These are only recommendations, and you should feel free to use other formatting guidelines and conventions if you have a preference for them. 4 | 5 | 本节列出了 Move 团队认为有用的一些基本的 Move 编码约定。这些只是建议,如果你喜欢其他格式指南和约定,你可以随时使用它们。 6 | 7 | ## 命名(Naming) 8 | 9 | - **Module names**: should be lower snake case, e.g., `fixed_point32`, `vector`. 10 | - **Type names**: should be camel case if they are not a native type, e.g., `Coin`, `RoleId`. 11 | - **Function names**: should be lower snake case, e.g., `destroy_empty`. 12 | - **Constant names**: should be upper snake case, e.g., `REQUIRES_CAPABILITY`. 13 | - Generic types should be descriptive, or anti-descriptive where appropriate, e.g., `T` or `Element` for the Vector generic type parameter. Most of the time the "main" type in a module should be the same name as the module e.g., `option::Option`, `fixed_point32::FixedPoint32`. 14 | - **Module file names**: should be the same as the module name e.g., `Option.move`. 15 | - **Script file names**: should be lower snake case and should match the name of the “main” function in the script. 16 | - **Mixed file names**: If the file contains multiple modules and/or scripts, the file name should be lower snake case, where the name does not match any particular module/script inside. 17 | 18 |
19 | 20 | - **模块名称**:应该使用小写的蛇形命名法,例如:`fixed_point32`、`vector`。 21 | - **类型名称**:如果不是原生数据类型,则应使用驼峰命名法,例如:`Coin`、`RoleId`。 22 | - **函数名称**:应该使用小写的蛇形命名法,例如:`destroy_empty`。 23 | - **常量名称**:应该使用大写的蛇形命名法,例如:`REQUIRES_CAPABILITY`。 24 | - 泛型类型应该具备描述性,当然在适当的情况下也可以是反描述性的,例如:Vector 泛型类型的参数可以是 `T` 或 `Element`。大多数情况下,模块中的“主”类型命名应该与模块名相同,例如:`option::Option`,`fixed_point32::FixedPoint32`。 25 | - **模块文件名称**:应该与模块名相同,例如:`Option.move`。 26 | - **脚本文件名称**:应该使用小写的蛇形命名法,并且应该与脚本中的“主”函数名匹配。 27 | - **混合文件名称**:如果文件包含多个模块和/或脚本,文件命名应该使用小写的蛇形命名法,并且不需要与内部的任何特定模块/脚本名匹配。 28 | 29 | ## 导入(Imports) 30 | 31 | - All module `use` statements should be at the top of the module. 32 | - Functions should be imported and used fully qualified from the module in which they are declared, and not imported at the top level. 33 | - Types should be imported at the top-level. Where there are name clashes, `as` should be used to rename the type locally as appropriate. 34 | 35 | For example, if there is a module: 36 | 37 | - 所有模块的 `use` 语句都应该位于模块的顶部。 38 | - 函数应该从声明它们的模块中完全限定地导入和使用, 而不是在顶部导入。 39 | - 类型应该在顶部导入。如果存在名称冲突,应使用 `as` 在本地适当地重命名类型。 40 | 41 | 例如,如果有一个模块: 42 | 43 | ```move 44 | module 0x1::foo { 45 | struct Foo { } 46 | const CONST_FOO: u64 = 0; 47 | public fun do_foo(): Foo { Foo{} } 48 | ... 49 | } 50 | ``` 51 | 52 | this would be imported and used as: 53 | 54 | 此时将被导入并使用: 55 | 56 | ```move 57 | module 0x1::bar { 58 | use 0x1::foo::{Self, Foo}; 59 | 60 | public fun do_bar(x: u64): Foo { 61 | if (x == 10) { 62 | foo::do_foo() 63 | } else { 64 | abort 0 65 | } 66 | } 67 | ... 68 | } 69 | ``` 70 | 71 | And, if there is a local name-clash when importing two modules: 72 | 73 | 并且,如果在导入两个模块时存在本地名称冲突: 74 | 75 | ```move 76 | module other_foo { 77 | struct Foo {} 78 | ... 79 | } 80 | 81 | module 0x1::importer { 82 | use 0x1::other_foo::Foo as OtherFoo; 83 | use 0x1::foo::Foo; 84 | ... 85 | } 86 | ``` 87 | 88 | ## 注释(Comments) 89 | 90 | - Each module, struct, and public function declaration should be commented. 91 | - Move has doc comments `///`, regular single-line comments `//`, block comments `/* */`, and block doc comments `/** */`. 92 | 93 |
94 | 95 | - 每个模块、结构体和公共函数声明都应该有对应的注释。 96 | - Move 有文档注释 `///`,常规单行注释 `//`,块注释 `/* */`,和块文档注释 `/** */`。 97 | 98 | ## 格式化(Formatting) 99 | 100 | The Move team plans to write an autoformatter to enforce formatting conventions. However, in the meantime: 101 | 102 | - Four space indentation should be used except for `script` and `address` blocks whose contents should not be indented. 103 | - Lines should be broken if they are longer than 100 characters. 104 | - Structs and constants should be declared before all functions in a module. 105 | 106 |
107 | 108 | Move 团队计划编写一个自动格式化程序来执行格式化约定。然而,在此期间: 109 | 110 | - 除 `script` 和 `address` 块外,其他的内容应使用四个空格的缩进。 111 | - 每行代码,如果超过 100 个字符,应该换行。 112 | - 结构体和常量应该在模块中的所有函数之前声明。 113 | -------------------------------------------------------------------------------- /src/conditionals.md: -------------------------------------------------------------------------------- 1 | # 条件语句 (Conditionals) 2 | 3 | An `if` expression specifies that some code should only be evaluated if a certain condition is true. For example: 4 | 5 | `if` 语句可以用来指定一块代码块,但只在判断条件(condition)为true时才会被执行。例如: 6 | 7 | ```move 8 | if (x > 5) x = x - 5 9 | ``` 10 | 11 | The condition must be an expression of type `bool`. 12 | 13 | An `if` expression can optionally include an `else` clause to specify another expression to evaluate when the condition is false. 14 | 15 | 条件语句(condition)必须是 `bool` 类型的表达式。 16 | 17 | `if` 语句可选包含 `else` 子句,以指定当条件(condition)为 false 时要执行的另一个代码块。 18 | 19 | ```move 20 | if (y <= 10) y = y + 1 else y = 10 21 | ``` 22 | 23 | Either the "true" branch or the "false" branch will be evaluated, but not both. Either branch can be a single expression or an expression block. 24 | 25 | The conditional expressions may produce values so that the `if` expression has a result. 26 | 27 | 无论是"true"分支还是"false"分支都会被执行,但不会同时执行.其中任何一个分支都可以是单行代码或代码块。条件表达式会产生值,所以 `if` 表达式会有一个结果。 28 | 29 | ```move 30 | let z = if (x < 100) x else 100; 31 | ``` 32 | 33 | The expressions in the true and false branches must have compatible types. For example: 34 | 35 | true 和 false 分支的表达式类型必须是一致的,例如: 36 | 37 | ```move= 38 | // x和y必须是u64整型 39 | // x and y must be u64 integers 40 | let maximum: u64 = if (x > y) x else y; 41 | 42 | // 错误!分支的类型不一致 43 | // (ERROR! branches different types) 44 | let z = if (maximum < 10) 10u8 else 100u64; 45 | 46 | // 错误!分支的类型不一致,false-branch默认是()不是u64 47 | // ERROR! branches different types, as default false-branch is () not u64 48 | if (maximum >= 10) maximum; 49 | ``` 50 | 51 | If the `else` clause is not specified, the false branch defaults to the unit value. The following are equivalent: 52 | 53 | 如果`else`子句未定义,false分支默认为 unit 。下面的例子是相等价的: 54 | 55 | ```move 56 | if (condition) true_branch // implied default: else () 57 | if (condition) true_branch else () 58 | ``` 59 | 60 | Commonly, [`if` expressions](./conditionals.md) are used in conjunction with expression blocks. 61 | 62 | 一般来说, [`if` 表达式](./conditionals.md)与多个表达式块结合使用. 63 | 64 | ```move 65 | let maximum = if (x > y) x else y; 66 | if (maximum < 10) { 67 | x = x + 10; 68 | y = y + 10; 69 | } else if (x >= 10 && y >= 10) { 70 | x = x - 10; 71 | y = y - 10; 72 | } 73 | ``` 74 | 75 | ## 条件语句的语法 (Grammar for Conditionals) 76 | 77 | > *if-expression* → **if (** *expression* **)** *expression* *else-clause**opt* 78 | > *else-clause* → **else** *expression* 79 | -------------------------------------------------------------------------------- /src/constants.md: -------------------------------------------------------------------------------- 1 | # 常量 (Constants) 2 | 3 | Constants are a way of giving a name to shared, static values inside of a `module` or `script`. 4 | 5 | The constant's must be known at compilation. The constant's value is stored in the compiled module 6 | or script. And each time the constant is used, a new copy of that value is made. 7 | 8 | 常量是一种对 `module` 或 `script` 内的共享静态值进行命名的方法(类似变量,但值不变,译者注)。 9 | 10 | 常量必须在编译时知道。常量的值存储在编译模块或脚本中。每次使用该常量时,都会生成该值的新副本。 11 | 12 | ## 声明 (Declaration) 13 | 14 | Constant declarations begin with the `const` keyword, followed by a name, a type, and a value. They 15 | can exist in either a script or module 16 | 17 | 常量声明以 `const` 关键字开头,后跟名称、类型和值。他们可以存在于脚本或模块中 18 | 19 | ```text 20 | const : = ; 21 | ``` 22 | 23 | 例如 24 | 25 | ```move= 26 | script { 27 | 28 | const MY_ERROR_CODE: u64 = 0; 29 | 30 | fun main(input: u64) { 31 | assert!(input > 0, MY_ERROR_CODE); 32 | } 33 | 34 | } 35 | 36 | address 0x42 { 37 | module example { 38 | 39 | const MY_ADDRESS: address = @0x42; 40 | 41 | public fun permissioned(s: &signer) { 42 | assert!(std::signer::address_of(s) == MY_ADDRESS, 0); 43 | } 44 | 45 | } 46 | } 47 | ``` 48 | 49 | ## 命名 (Naming) 50 | 51 | Constants must start with a capital letter `A` to `Z`. After the first letter, constant names can 52 | contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, or digits `0` to `9`. 53 | 54 | 常量必须以大写字母 `A` 到 `Z` 开头。在第一个字母之后,常量名可以包含下划线 `_`、字母 `a` 到 `z`、字母 `A` 到 `Z` 或数字 `0` 到 `9`。 55 | 56 | ```move 57 | const FLAG: bool = false; 58 | const MY_ERROR_CODE: u64 = 0; 59 | const ADDRESS_42: address = @0x42; 60 | ``` 61 | 62 | Even though you can use letters `a` to `z` in a constant. The 63 | [general style guidelines](./coding-conventions.md) are to use just uppercase letters `A` to `Z`, 64 | with underscores `_` between each word. 65 | 66 | 虽然你可以在常量中使用字母 `a` 到 `z`。但[通用风格指南](./coding-conventions.md) 只使用大写字母 `A` 到 `Z`,每个单词之间有下划线`_`。 67 | 68 | This naming restriction of starting with `A` to `Z` is in place to give room for future language features. It may or may not be removed later. 69 | 70 | 这种以 `A` 到 `Z` 开头的命名限制是为了给未来的语言特性留出空间。此限制未来可能会保留或删除。 71 | 72 | ## 可见性 (Visibility) 73 | 74 | `public` constants are not currently supported. `const` values can be used only in the declaring 75 | module. 76 | 77 | 当前不支持 `public` 常量。 `const` 值只能在声明的模块中使用。 78 | 79 | ## 有效表达式 (Valid Expressions) 80 | 81 | Currently, constants are limited to the primitive types `bool`, `u8`, `u64`, `u128`, `address`, and 82 | `vector`. Future support for other `vector` values (besides the "string"-style literals) will come later. 83 | 84 | 目前,常量仅限于原始类型 `bool`、`u8`、`u64`、`u128`、`address` 和`vector`。其他 `vector` 值(除了“string”风格的字面量)将在不远的将来获得支持。 85 | 86 | ### 值 (Values) 87 | 88 | Commonly, `const`s are assigned a simple value, or literal, of their type. For example 89 | 90 | 通常,`const` (常量)会被分配一个对应类型的简单值或字面量。例如 91 | 92 | ```move 93 | const MY_BOOL: bool = false; 94 | const MY_ADDRESS: address = @0x70DD; 95 | const BYTES: vector = b"hello world"; 96 | const HEX_BYTES: vector = x"DEADBEEF"; 97 | ``` 98 | 99 | ### 复杂表达式 (Complex Expressions) 100 | 101 | 102 | In addition to literals, constants can include more complex expressions, as long as the compiler is 103 | able to reduce the expression to a value at compile time. 104 | 105 | Currently, equality operations, all boolean operations, all bitwise operations, and all arithmetic 106 | operations can be used. 107 | 108 | 除了字面量,常量还可以包含更复杂的表达式,只要编译器能够在编译时将表达式归纳(reduce)为一个值。 109 | 110 | 目前,相等运算、所有布尔运算、所有按位运算和所有算术运算可以使用。 111 | 112 | ```move 113 | const RULE: bool = true && false; 114 | const CAP: u64 = 10 * 100 + 1; 115 | const SHIFTY: u8 = { 116 | (1 << 1) * (1 << 2) * (1 << 3) * (1 << 4) 117 | }; 118 | const HALF_MAX: u128 = 340282366920938463463374607431768211455 / 2; 119 | const EQUAL: bool = 1 == 1; 120 | ``` 121 | 122 | If the operation would result in a runtime exception, the compiler will give an error that it is 123 | unable to generate the constant's value 124 | 125 | 如果操作会导致运行时异常,编译器会给出无法生成常量值的错误。 126 | 127 | ```move 128 | const DIV_BY_ZERO: u64 = 1 / 0; // error! 129 | const SHIFT_BY_A_LOT: u64 = 1 << 100; // error! 130 | const NEGATIVE_U64: u64 = 0 - 1; // error! 131 | ``` 132 | 133 | Note that constants cannot currently refer to other constants. This feature, along with support for 134 | other expressions, will be added in the future. 135 | 136 | 请注意,常量当前不能引用其他常量。此功能会在将来和支持其他表达方式一起被补充。 137 | -------------------------------------------------------------------------------- /src/diagrams/move_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/move-dao/move-book-zh/09268ba66c2e666c9bacece9c25e48fd03e30684/src/diagrams/move_state.png -------------------------------------------------------------------------------- /src/diagrams/solidity_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/move-dao/move-book-zh/09268ba66c2e666c9bacece9c25e48fd03e30684/src/diagrams/solidity_state.png -------------------------------------------------------------------------------- /src/equality.md: -------------------------------------------------------------------------------- 1 | 2 | # 等式 (Equality) 3 | 4 | Move supports two equality operations `==` and `!=` 5 | 6 | Move 支持两种等式操作: `==` 和 `!=` 7 | 8 | ## 操作 (Operations) 9 | 10 | | Syntax | Operation | Description | 11 | | ------ | --------- | --------------------------------------------------------------------------- | 12 | | `==` | equal | Returns `true` if the two operands have the same value, `false` otherwise | 13 | | `!=` | not equal | Returns `true` if the two operands have different values, `false` otherwise | 14 | 15 | | 语法 | 操作 | 描述 | 16 | | ------ | --------- | --------------------------------------------------------------------------- | 17 | | `==` | 相等 | 如果两个操作数(operands)值相同,返回 `true` , 否则返回 `false` | 18 | | `!=` | 不相等 | 如果两个操作数(operands)值不相同,返回 `true` , 否则返回 `false` | 19 | 20 | ### 类型校验 (Typing) 21 | 22 | Both the equal (`==`) and not-equal (`!=`) operations only work if both operands are the same type. 23 | 24 | 只有当左右两个操作数类型相同,相等操作 (`==`) 与不等操作 (`!=`) 才能正常使用。 25 | 26 | ```move 27 | 0 == 0; // `true` 28 | 1u128 == 2u128; // `false` 29 | b"hello" != x"00"; // `true` 30 | ``` 31 | 32 | Equality and non-equality also work over user defined types! 33 | 34 | 等式与不等式也可以在用户自定义的类型下使用! 35 | 36 | ```move= 37 | address 0x42 { 38 | module example { 39 | struct S has copy, drop { f: u64, s: vector } 40 | 41 | fun always_true(): bool { 42 | let s = S { f: 0, s: b"" }; 43 | // 括号不是必需的,但为了清楚起见在此示例中添加了括号 44 | (copy s) == s 45 | } 46 | 47 | fun always_false(): bool { 48 | let s = S { f: 0, s: b"" }; 49 | // 括号不是必需的,但为了清楚起见在此示例中添加了括号 50 | (copy s) != s 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | If the operands have different types, there is a type checking error. 57 | 58 | 如果两边操作数的类型不同,则会出现类型检测错误。 59 | 60 | ```move 61 | 1u8 == 1u128; // 错误! 62 | // ^^^^^ 期望此变量的类型是 'u8' 63 | b"" != 0; // 错误! 64 | // ^ 期望此变量的类型是 'vector' 65 | ``` 66 | 67 | ### 引用变量的类型校验 (Typing with references) 68 | 69 | When comparing [references](./references.md), the type of the reference (immutable or mutable) does 70 | not matter. This means that you can compare an immutable `&` reference with a mutable one `&mut` of 71 | the same underlying type. 72 | 73 | 当比较[引用变量](./references.md)时,引用的类别(不可变更的或可变更的(immutable or mutable))无关紧要。这意味着我们可以拿一个不可变更的 `&` 引用变量和另一个有相同相关类型的可变更的 `&mut ` 引用变量进行比较。 74 | 75 | ```move 76 | let i = &0; 77 | let m = &mut 1; 78 | 79 | i == m; // `false` 80 | m == i; // `false` 81 | m == m; // `true` 82 | i == i; // `true` 83 | ``` 84 | 85 | The above is equivalent to applying an explicit freeze to each mutable reference where needed 86 | 87 | 在需要时,对每个可变引用使用显式冻结(explicit freeze)的结果与上述情况一致。 88 | 89 | ```move 90 | let i = &0; 91 | let m = &mut 1; 92 | 93 | i == freeze(m); // `false` 94 | freeze(m) == i; // `false` 95 | m == m; // `true` 96 | i == i; // `true` 97 | ``` 98 | But again, the underlying type must be the same type 99 | 100 | 但同样的,我们需要两边操作数的类型一致 101 | 102 | ```move 103 | let i = &0; 104 | let s = &b""; 105 | 106 | i == s; // 错误! 107 | // ^ 期望此变量的类型是 '&u64' 108 | ``` 109 | 110 | ## 限制 (Restrictions) 111 | 112 | Both `==` and `!=` consume the value when comparing them. As a result, the type system enforces that 113 | the type must have [`drop`](./abilities.md). Recall that without the [`drop` ability](./abilities.md), 114 | ownership must be transferred by the end of the function, and such values can only be explicitly destroyed 115 | within their declaring module. If these were used directly with either equality `==` or non-equality `!=`, 116 | the value would be destroyed which would break [`drop` ability](./abilities.md) safety guarantees! 117 | 118 | `==` 和 `!=` 会在比较不同变量的时候消耗 (consume)它们所包含的值,所以 Move 的类型系统会强制要求这些类型含有[`drop` 能力](./abilities.md)。回想一下,变量在没有[`drop` 能力](./abilities.md)时,所有权必须在函数结束前进行转移,而且这些值只能在其声明模块中被明确销毁(explicitly destroyed)。如果它们被直接使用于等式 `==` 或不等式 `!=` ,其值会被销毁并且这会打破[`drop` 能力](./abilities.md)的安全保证! 119 | 120 | ```move= 121 | address 0x42 { 122 | module example { 123 | struct Coin has store { value: u64 } 124 | fun invalid(c1: Coin, c2: Coin) { 125 | c1 == c2 // 错误! 126 | // ^^ ^^ 这些资源将会被销毁! 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | 133 | But, a programmer can _always_ borrow the value first instead of directly comparing the value, and 134 | reference types have the [`drop` ability](./abilities.md). For example 135 | 136 | 然而, 程序员 _总是_ 可以优先借用变量的值,而不直接比较它们的值。这样一来,引用变量的类型将会拥有[`drop` 能力](./abilities.md)。例如: 137 | 138 | ```move= 139 | address 0x42 { 140 | module example { 141 | struct Coin as store { value: u64 } 142 | fun swap_if_equal(c1: Coin, c2: Coin): (Coin, Coin) { 143 | let are_equal = &c1 == &c2; // 合规范的 144 | if (are_equal) (c2, c1) else (c1, c2) 145 | } 146 | } 147 | } 148 | ``` 149 | 150 | ## 避免额外的复制 (Avoid Extra Copies) 151 | 152 | While a programmer _can_ compare any value whose type has [`drop`](./abilities.md), a programmer 153 | should often compare by reference to avoid expensive copies. 154 | 155 | 当程序员 _可以_ 比较其类型含有[`drop` 能力](./abilities.md)的任意值时,他们应该尽可能多地使用引用变量来比较,以此来避免昂贵的复制。 156 | 157 | ```move= 158 | let v1: vector = function_that_returns_vector(); 159 | let v2: vector = function_that_returns_vector(); 160 | assert!(copy v1 == copy v2, 42); 161 | // ^^^^ ^^^^ 162 | use_two_vectors(v1, v2); 163 | 164 | let s1: Foo = function_that_returns_large_struct(); 165 | let s2: Foo = function_that_returns_large_struct(); 166 | assert!(copy s1 == copy s2, 42); 167 | // ^^^^ ^^^^ 168 | use_two_foos(s1, s2); 169 | ``` 170 | 171 | This code is perfectly acceptable (assuming `Foo` has [`drop`](./abilities.md)), just not efficient. 172 | The highlighted copies can be removed and replaced with borrows 173 | 174 | 以上代码是完全可以接受的(假设`Foo`具备[`drop`](./abilities.md)能力),但它不是最有效的写法。突出显示的副本可以删除并替换为借用。 175 | 176 | ```move= 177 | let v1: vector = function_that_returns_vector(); 178 | let v2: vector = function_that_returns_vector(); 179 | assert!(&v1 == &v2, 42); 180 | // ^ ^ 181 | use_two_vectors(v1, v2); 182 | 183 | let s1: Foo = function_that_returns_large_struct(); 184 | let s2: Foo = function_that_returns_large_struct(); 185 | assert!(&s1 == &s2, 42); 186 | // ^ ^ 187 | use_two_foos(s1, s2); 188 | ``` 189 | 190 | The efficiency of the `==` itself remains the same, but the `copy`s are removed and thus the program is more efficient. 191 | 192 | `==` 本身的效率还是和之前一样,但是 `copy` 操作被移除后整个程序会比之前更有效率。 193 | -------------------------------------------------------------------------------- /src/friends.md: -------------------------------------------------------------------------------- 1 | # 友元函数(Friends) 2 | 3 | The `friend` syntax is used to declare modules that are trusted by the current module. 4 | A trusted module is allowed to call any function defined in the current module that have the `public(friend)` visibility. 5 | For details on function visibilities, please refer to the *Visibility* section in [Functions](./functions.md). 6 | 7 | 友元语法用于声明当前模块信任的其它模块。受信任的模块可以调用当前模块中定义的任何具有`公开(友元)`可见性的函数。有关函数可见性的详细信息,请参阅[函数](./functions.md)中的可见性部分。 8 | 9 | ## 友元声明(Friend declaration) 10 | 11 | A module can declare other modules as friends via friend declaration statements, in the format of 12 | 13 | 一个模块可以通过友元声明语句将其他模块声明为友元,格式为: 14 | 15 | - `friend ` — friend declaration using fully qualified module name like the example below, or 16 | - `friend —` 使用完全合格的模块名称的友元声明,如下例所示,或 17 | 18 | ``` 19 | address 0x42 { 20 | module a { 21 | friend 0x42::b; 22 | } 23 | } 24 | ``` 25 | 26 | - `friend ` — friend declaration using a module name alias, where the module alias is introduced via the `use` statement. 27 | - `friend —` 使用模块名称别名的友元声明,其中模块别名是通过use语句引入的。 28 | 29 | ```move 30 | address 0x42 { 31 | module a { 32 | use 0x42::b; 33 | friend b; 34 | } 35 | } 36 | ``` 37 | 38 | A module may have multiple friend declarations, and the union of all the friend modules forms the friend list. 39 | In the example below, both `0x42::B` and `0x42::C` are considered as friends of `0x42::A`. 40 | 41 | 一个模块可能有多个友元声明,所有好友模块的并集形成友元列表。在下面的示例中`,0x42::B`和`0x42::C`都被视为 的友元函数`0x42::A`。 42 | 43 | ```move 44 | address 0x42 { 45 | module a { 46 | friend 0x42::b; 47 | friend 0x42::c; 48 | } 49 | } 50 | ``` 51 | 52 | Unlike `use` statements, `friend` can only be declared in the module scope and not in the expression block scope. 53 | `friend` declarations may be located anywhere a top-level construct (e.g., `use`, `function`, `struct`, etc.) is allowed. 54 | However, for readability, it is advised to place friend declarations near the beginning of the module definition. 55 | 56 | 与`use`语句不同,`friend`只能在模块作用域内声明,而不能在表达式块的作用域内声明。`friend`声明可以位于允许顶层构造的任何位置(例如, `use`, `function,struct`等)是被允许的。但是,为了可读性,建议将友元声明放在模块定义的开头附近。 57 | 58 | Note that the concept of friendship does not apply to Move scripts: 59 | - A Move script cannot declare `friend` modules as doing so is considered meaningless: there is no mechanism to call the function defined in a script. 60 | - A Move module cannot declare `friend` scripts as well because scripts are ephemeral code snippets that are never published to global storage. 61 | 62 | 请注意,友元关系(friendship)的概念不适用于 Move 脚本: 63 | - `Move` 脚本不能声明`friend`模块,因为这样做被认为是无意义的:没有机制可以调用脚本中定义的函数。 64 | - `Move` 模块也不能声明`friend`脚本,因为脚本是永远不会发布到全局存储的临时代码片段。 65 | 66 | ### 友元声明规则(Friend declaration rules) 67 | Friend declarations are subject to the following rules: 68 | 友元声明须遵守以下规则: 69 | 70 | - A module cannot declare itself as a friend 71 | - 一个模块不能将自己声明为友元。 72 | 73 | ```move= 74 | address 0x42 { 75 | module m { friend Self; // 错误! } 76 | // ^^^^ 不能将自己声明为友元 77 | } 78 | 79 | address 0x43 { 80 | module m { friend 0x43::M; // 错误! } 81 | // ^^^^^^^ 不能将自己声明为友元 82 | } 83 | ``` 84 | 85 | - Friend modules must be known by the compiler 86 | - 编译器必须知道友元模块 87 | 88 | ```move= 89 | address 0x42 { 90 | module m { friend 0x42::nonexistent; // 错误! } 91 | // ^^^^^^^^^^^^^^^^^ 未绑定的模块 '0x42::nonexistent' 92 | } 93 | ``` 94 | 95 | - Friend modules must be within the same account address. (Note: this is not a technical requirement but rather a policy decision which *may* be relaxed later.) 96 | 97 | - 友元模块必须在同一个账号地址内。(注:这不是技术要求,而是以后可能放宽的决策。) 98 | 99 | ```move 100 | address 0x42 { 101 | module m {} 102 | } 103 | 104 | address 0x43 { 105 | module n { friend 0x42::m; // 错误! } 106 | // ^^^^^^^ 不能声明当前地址外的模块作为友元 107 | } 108 | ``` 109 | 110 | - 友元关系不能创建循环模块依赖关系(Friends relationships cannot create cyclic module dependencies) 111 | 112 | Cycles are not allowed in the friend relationships, e.g., the relation `0x2::a` friends `0x2::b` friends `0x2::c` friends `0x2::a` is not allowed. 113 | More generally, declaring a friend module adds a dependency upon the current module to the friend module (because the purpose is for the friend to call functions in the current module). 114 | If that friend module is already used, either directly or transitively, a cycle of dependencies would be created. 115 | 116 | 友元关系中不允许循环,例如 `0x2::a` 友元 `0x2::b` 友元 `0x2::c` 友元`0x2::a`是不允许的。更普遍地,声明一个友元模块会将对当前模块的依赖添加到友元模块(因为目的是让友元调用当前模块中的函数)。如果该友元模块已被直接或传递地使用,则将形成一个依赖循环。 117 | 118 | ```move 119 | address 0x2 { 120 | module a { 121 | use 0x2::c; 122 | friend 0x2::b; 123 | 124 | public fun a() { 125 | c::c() 126 | } 127 | } 128 | 129 | module b { 130 | friend 0x2::c; // 错误! 131 | // ^^^^^^ 这个友元关系形成了一个依赖循环: '0x2::a' 使用了 '0x2::c' 但'0x2::b' 同时是 '0x2::a'和'0x2::b' 的友元 132 | } 133 | 134 | module c { 135 | public fun c() {} 136 | } 137 | } 138 | ``` 139 | 140 | - The friend list for a module cannot contain duplicates. 141 | - 模块的友元列表不能包含重复项。 142 | 143 | ```move= 144 | address 0x42 { 145 | module a {} 146 | 147 | module m { 148 | use 0x42::a as aliased_a; 149 | friend 0x42::A; 150 | friend aliased_a; // 错误! 151 | // ^^^^^^^^^ 重复的友元声明 '0x42::a'. 模块内的友元声明必须是唯一的 152 | } 153 | } 154 | ``` 155 | -------------------------------------------------------------------------------- /src/global-storage-structure.md: -------------------------------------------------------------------------------- 1 | # 全局存储 —— 结构(Global Storage - Structure) 2 | 3 | The purpose of Move programs is to [read from and write to](./global-storage-operators.md) tree-shaped persistent global storage. Programs cannot access the filesystem, network, or any other data outside of this tree. 4 | 5 | Move 程序的目的是[读取和写入](./global-storage-operators.md)树形的持久全局存储。程序不能访问文件系统、网络或任何此树以外的数据。 6 | 7 | In pseudocode, the global storage looks something like: 8 | 9 | 在伪代码中,全局存储看起来像: 10 | 11 | ```move 12 | struct GlobalStorage { 13 | resources: Map<(address, ResourceType), ResourceValue> 14 | modules: Map<(address, ModuleName), ModuleBytecode> 15 | } 16 | ``` 17 | 18 | Structurally, global storage is a [forest](https://en.wikipedia.org/wiki/Tree_(graph_theory)) consisting of trees rooted at an account [`address`](./address.md). Each address can store both [resource](./structs-and-resources.md) data values and [module](./modules-and-scripts.md) code values. As the pseudocode above indicates, each `address` can store at most one resource value of a given type and at most one module with a given name. 19 | 20 | 从结构上讲,全局存储是一个[森林(forest)](https://en.wikipedia.org/wiki/Tree_(graph_theory)),这个森林由以账户[地址(`address`)](./address.md)为根的树组成。每个地址可以存储[资源(resource)](./structs-and-resources.md)数据和[模块(module)](./modules-and-scripts.md)代码。如上面的伪代码所示,每个地址(`address`)最多可以存储一个给定类型的资源值,最多可以存储一个给定名称的模块。 21 | -------------------------------------------------------------------------------- /src/integers.md: -------------------------------------------------------------------------------- 1 | # 整数 (Integers) 2 | 3 | Move supports three unsigned integer types: `u8`, `u64`, and `u128`. Values of these types range from 0 to a maximum that depends on the size of the type. 4 | 5 | | Type | Value Range | 6 | | -------------------------------- | ------------------------ | 7 | | Unsigned 8-bit integer, `u8` | 0 to 28 - 1 | 8 | | Unsigned 64-bit integer, `u64` | 0 to 264 - 1 | 9 | | Unsigned 128-bit integer, `u128` | 0 to 2128 - 1 | 10 | 11 | 12 | Move 支持三种无符号整数类型:`u8`、`u64` 和 `u128`。这些类型的值范围从 0 到最大值,最大值的具体取值取决于整数类型。 13 | 14 | | 类型 | 取值范围 | 15 | |---------------------------|--------------------------| 16 | | 无符号 8位 整数, `u8` | 0 to 28 - 1 | 17 | | 无符号 64位 整数, `u64` | 0 to 264 - 1 | 18 | | 无符号 128位 整数, `u128` | 0 to 2128 - 1 | 19 | 20 | 21 | ## 字面值(Literal) 22 | 23 | Literal values for these types are specified either as a sequence of digits (e.g.,`112`) or as hex literals, e.g., `0xFF`. The type of the literal can optionally be added as a suffix, e.g., `112u8`. If the type is not specified, the compiler will try to infer the type from the context where the literal is used. If the type cannot be inferred, it is assumed to be `u64`. 24 | 25 | If a literal is too large for its specified (or inferred) size range, an error is reported. 26 | 27 | (在Move中)这些类型的字面值指定为数字序列(例如:112)或十六进制文字(例如:0xFF), 可以选择将字面值的类型定义为后缀, 例如 `112u8`。如果未指定类型,编译器将尝试从使用字面值的上下文推断类型。如果无法推断类型,则默认为 `u64。 28 | 29 | 如果字面值太大,超出其指定的(或推断的)大小范围,则会报错。 30 | 31 | ### 例如: 32 | 33 | ```jsx 34 | // literals with explicit annotations; 35 | let explicit_u8 = 1u8; 36 | let explicit_u64 = 2u64; 37 | let explicit_u128 = 3u128; 38 | 39 | // literals with simple inference 40 | let simple_u8: u8 = 1; 41 | let simple_u64: u64 = 2; 42 | let simple_u128: u128 = 3; 43 | 44 | // literals with more complex inference 45 | let complex_u8 = 1; // inferred: u8 46 | // right hand argument to shift must be u8 47 | let _unused = 10 << complex_u8; 48 | 49 | let x: u8 = 0; 50 | let complex_u8 = 2; // inferred: u8 51 | // arguments to `+` must have the same type 52 | let _unused = x + complex_u8; 53 | 54 | let complex_u128 = 3; // inferred: u128 55 | // inferred from function argument type 56 | function_that_takes_u128(complex_u128); 57 | 58 | // literals can be written in hex 59 | let hex_u8: u8 = 0x1; 60 | let hex_u64: u64 = 0xCAFE; 61 | let hex_u128: u128 = 0xDEADBEEF; 62 | ``` 63 | 64 | ## 运算集 (Operations) 65 | 66 | ### 算术运算 (Arithmetic) 67 | 68 | Each of these types supports the same set of checked arithmetic operations. For all of these operations, both arguments (the left and right side operands) must be of the same type. If you need to operate over values of different types, you will need to first perform a cast. Similarly, if you expect the result of the operation to be too large for the integer type, perform a cast to a larger size before performing the operation. 69 | 70 | 每一种(无符号整数)类型都支持相同算术运算集。对于所有这些运算,两个参数(左侧和右侧操作数)必须是同一类型。如果您需要对不同类型的值进行运算,则需要首先执行强制转换。同样,如果您预计运算结果对于当下整数类型来说太大,请在执行运算之前将之转换为更大的整数类型。 71 | 72 | All arithmetic operations abort instead of behaving in a way that mathematical integers would not (e.g., overflow, underflow, divide-by-zero). 73 | 74 | | Syntax | Operation | Aborts If | 75 | | ------ | ------------------- | ---------------------------------------- | 76 | | `+` | addition | Result is too large for the integer type | 77 | | `-` | subtraction | Result is less than zero | 78 | | `*` | multiplication | Result is too large for the integer type | 79 | | `%` | modular division | The divisor is `0` | 80 | | `/` | truncating division | The divisor is `0` | 81 | 82 | ### [Bitwise](https://move-language.github.io/move/integers.html#bitwise) 83 | 84 | 算术运算在遇到异常时将会中止,而不是以上溢、下溢、被零除等数学整数未定义的的方式输出结果。 85 | 86 | | 句法 | 操作 | 中止条件 | 87 | | ------ | ------------| ---------------------------------------- | 88 | | `+` | 加法 | 结果对于整数类型来说太大了 | 89 | | `-` | 减法 | 结果小于零 | 90 | | `*` | 乘法 | 结果对于整数类型来说太大了 | 91 | | `%` | 取余运算 | 除数为 `0` | 92 | | `/` | 截断除法 | 除数为 `0` | 93 | 94 | ### 位运算 (Bitwise) 95 | 96 | The integer types support the following bitwise operations that treat each number as a series of individual bits, either 0 or 1, instead of as numerical integer values. 97 | 98 | Bitwise operations do not abort. 99 | 100 | | Syntax | Operation | Description 101 | |--------|------------|------------ 102 | | `&` | bitwise and| Performs a boolean and for each bit pairwise 103 | | `|` | bitwise or | Performs a boolean or for each bit pairwise 104 | | `^` | bitwise xor| Performs a boolean exclusive or for each bit pairwise 105 | 106 | 107 | 整数类型支持下列位运算,即将每个数字视为一系列单独的位:0 或 1,而不是整型数值。 108 | 109 | 位运算不会中止。 110 | 111 | | 句法 | 操作 | 描述 | 112 | | ------ | ----------- | ----------------------------------------------------- | 113 | | `&` | 按位 和 | 对每个位成对执行布尔值和 | 114 | | `|` | 按位或 | 对每个位成对执行布尔值或 115 | | `^` | 按位 异与 | 对每个位成对执行布尔异或 | 116 | 117 | ### 位移 (Bit shift) 118 | 119 | Similar to the bitwise operations, each integer type supports bit shifts. But unlike the other operations, the righthand side operand (how many bits to shift by) must *always* be a `u8` and need not match the left side operand (the number you are shifting). 120 | 121 | Bit shifts can abort if the number of bits to shift by is greater than or equal to `8`, `64`, or `128` for `u8`, `u64`, and `u128` respectively. 122 | 123 | | Syntax | Operation | Aborts if | 124 | | ------ | ----------- | ------------------------------------------------------------ | 125 | | `<<` | shift left | Number of bits to shift by is greater than the size of the integer type | 126 | | `>>` | shift right | Number of bits to shift by is greater than the size of the integer type | 127 | 128 | 与按位运算类似,每种整数类型都支持位移(bit shifts)。但与其他运算不同的是,右侧操作数(要移位多少位)必须始终是 `u8`  并且不需要与左侧操作数类型(您要移位的数字)匹配。 129 | 130 | 如果要移位的位数分别大于或等于 `8`、`64`, `u128` 或 `128` 的 `u8`, `u64`, 则移位可以中止。 131 | 132 | | 句法 | 操作 | 中止条件 | 133 | | ------ | ----------- | ------------------------------------------------------------ | 134 | | `<<` | 左移 | 要移位的位数大于整数类型的大小 | 135 | | `>>` | 右移 | 要移位的位数大于整数类型的大小 | 136 | 137 | ### 比较运算 (Comparisons) 138 | 139 | Integer types are the *only* types in Move that can use the comparison operators. Both arguments need to be of the same type. If you need to compare integers of different types, you will need to [cast](https://move-language.github.io/move/integers.html#casting) one of them first. 140 | 141 | Comparison operations do not abort. 142 | 143 | | Syntax | Operation | 144 | | ------ | ------------------------ | 145 | | `<` | less than | 146 | | `>` | greater than | 147 | | `<=` | less than or equal to | 148 | | `>=` | greater than or equal to | 149 | 150 | 整数类型是 Move 中唯一可以使用比较(Comparisons)运算符的类型。两个参数必须是同一类型。如果您需要比较不同类型的整数,则需要先转换其中一个。 151 | 152 | 比较操作不会中止。 153 | 154 | | 句法 | 操作 | 155 | | ------ | ------------------------ | 156 | | `<` | 小于 | 157 | | `>` | 大于 | 158 | | `<=` | 小于等于 | 159 | | `>=` | 大于等于 | 160 | 161 | ### 相等 (Equality) 162 | 163 | Like all types with [`drop`](https://move-language.github.io/move/abilities.html) in Move, all integer types support the ["equal"](https://move-language.github.io/move/equality.html) and ["not equal"](https://move-language.github.io/move/equality.html) operations. Both arguments need to be of the same type. If you need to compare integers of different types, you will need to [cast](https://move-language.github.io/move/integers.html#casting) one of them first. 164 | 165 | Equality operations do not abort. 166 | 167 | | Syntax | Operation | 168 | | ------ | --------- | 169 | | `==` | equal | 170 | | `!=` | not equal | 171 | 172 | For more details see the section on [equality](https://move-language.github.io/move/equality.html) 173 | 174 | 与 Move 中的所有具有[`drop`](./chapter_19_abilities.html)能力的类型一样,所有整数类型都支持 ["equal(等于)"](./chapter_11_equality.html) 和 ["not equal(不等于)](./chapter_11_equality.html)运算。两个参数必须是同一类型。如果您需要比较不同类型的整数,则需要先转换其中一个。 175 | 176 | 相等(Equality)运算不会中止。 177 | 178 | | 句法 | 操作 | 179 | | ------ | --------- | 180 | | `==` | 等于 | 181 | | `!=` | 不等于 | 182 | 183 | 更多细节可以参考[相等]([equality](https://move-language.github.io/move/equality.html))章节。 184 | 185 | ## 转换 (Casting) 186 | 187 | Integer types of one size can be cast to integer types of another size. Integers are the only types in Move that support casting. 188 | 189 | Casts *do not* truncate. Casting will abort if the result is too large for the specified type 190 | 191 | | Syntax | Operation | Aborts if | 192 | | ---------- | ---------------------------------------------------- | -------------------------------------- | 193 | | `(e as T)` | Cast integer expression `e` into an integer type `T` | `e` is too large to represent as a `T` | 194 | 195 | Here, the type of `e` must be `u8`, `u64`, or `u128` and `T` must be `u8`, `u64`, or `u128`. 196 | 197 | For example: 198 | 199 | - `(x as u8)` 200 | - `(2u8 as u64)` 201 | - `(1 + 3 as u128)` 202 | 203 | 一种大小的整数类型可以转换为另一种大小的整数类型。整数是 Move 中唯一支持强制转换的类型。 204 | 205 | 强制转换不会截断。如果结果对于指定类型来说太大,则转换将中止。 206 | 207 | | Syntax | 操作 | 中止条件 | 208 | | ---------- | ---------------------------------------------------- | -------------------------------------- | 209 | | `(e as T)` | 将整数表达式 `e` 转换为整数类型 `T` | `e` 太大而不能表示为 `T` | 210 | 211 | ## 所有权 (Ownership) 212 | 213 | As with the other scalar values built-in to the language, integer values are implicitly copyable, meaning they can be copied without an explicit instruction such as [`copy`](https://move-language.github.io/move/variables.html#move-and-copy). 214 | 215 | 与语言内置的其他标量值一样,整数值是隐式可复制的,这意味着它们可以在没有明确指令如[`copy`](./variables.md#move-and-copy)的情况下复制。 216 | -------------------------------------------------------------------------------- /src/introduction.md: -------------------------------------------------------------------------------- 1 | # 引言 (Introduction) 2 | 3 | Welcome to Move, a next generation language for secure, sandboxed, and formally verified programming. Its first use case is for the Diem blockchain, where Move provides the foundation for its implementation. Move allows developers to write programs that flexibly manage and transfer assets, while providing the security and protections against attacks on those assets. However, Move has been developed with use cases in mind outside a blockchain context as well. 4 | 5 | 欢迎来到Move的世界,Move是一种安全、沙盒式和形式化验证的下一代编程语言,它的第一个用例是 Diem 区块链(当时名字叫Libra, 脸书团队开发的项目, 译者注), Move 为其实现提供了基础。 Move 允许开发人员编写灵活管理和转移数字资产的程序,同时提供安全保护,防止对那些链上资产的攻击。不仅如此,Move 也可用于区块链世界之外的开发场景。 6 | 7 | Move takes its cue from [Rust](https://www.rust-lang.org/) by using resource types with move (hence the name) semantics as an explicit representation of digital assets, such as currency. 8 | 9 | Move 的诞生从[Rust](https://www.rust-lang.org/)中吸取了灵感,Move也是因为使用具有移动(move)语义的资源类型作为数字资产(例如货币)的显式表示而得名。 10 | 11 | ## Move是为谁准备的?(Who is Move for?) 12 | 13 | Move was designed and created as a secure, verified, yet flexible programming language. The first use of Move is for the implementation of the Diem blockchain. That said, the language is still evolving. Move has the potential to be a language for other blockchains, and even non-blockchain use cases as well. 14 | 15 | Move语言被设计和创建为安全、可验证, 同时兼顾灵活性的编程语言。Move的第一个应用场景是用于Diem区块链的开发。现在,Move语言仍在不断发展中。Move 还有成为其他区块链,甚至非区块链用例开发语言的潜质。 16 | 17 | Given custom Move modules will not be supported at the [launch](https://diem.com/white-paper/#whats-next) of the Diem Payment Network (DPN), we are targeting an early Move Developer persona. 18 | 19 | 鉴于在 Diem 支付网络 (DPN) [启动](https://diem.com/white-paper/#whats-next)时将不支持自定义 Move 模块(custom Move modules),我们的目标是早期的 Move 开发人员。 20 | 21 | The early Move Developer is one with some programming experience, who wants to begin understanding the core programming language and see examples of its usage. 22 | 23 | 早期的 Move 开发人员应该是具有一定编程经验的程序员,他们愿意了解编程语言核心,并探索它的用法。 24 | 25 | ### 爱好者 (Hobbyists) 26 | 27 | Understanding that the capability to create custom modules on the Diem Payment Network will not be available at launch, the hobbyist Move Developer is interested in learning the intricacies of the language. She will understand the basic syntax, the standard libraries available, and write example code that can be executed using the Move CLI. The Move Developer may even want to dig into understanding how the Move Virtual Machine executes the code she writes. 28 | 29 | 作为(Move语言)爱好者角色,首先需要明白在Diem支付网络上创建自定义模块(custom modules)是不可能的,其次,你还要对探索这门语言的复杂性保持兴趣。你将了解基本语法、可用的标准库,并编写可以用的Move CLI执行的示例代码。如果可能,你甚至可以去尝试体验Move虚拟机如何执行你自己编写的代码。 30 | 31 | ### 核心贡献者 (Core Contributor) 32 | 33 | Beyond a hobbyist wanting to stay ahead of the curve for the core programming language is someone who may want to [contribute](https://diem.com/en-US/cla-sign/) directly to Move. Whether this includes submitting language improvements or even, in the future, adding core modules available on the Diem Payment Network, the core contributor will understand Move at a deep level. 34 | 35 | 核心贡献者指那些超越爱好者并想在核心编程语言方面保持领先,还直接为 Move 做出[贡献](https://diem.com/en-US/cla-sign/)的人。无论是提交语言改进,甚至未来添加 Diem 支付网络上可用的核心模块等,核心贡献者都将深入了解Move。 36 | 37 | ### Move不适用于哪些人?(Who Move is currently not targeting) 38 | 39 | Currently, Move is not targeting developers who wish to create custom modules and contracts for use on the Diem Payment Network. We are also not targeting novice developers who expect a completely polished developer experience even in testing the language. 40 | 41 | 目前,Move 并不适用那些希望在在 Diem 支付网络上创建自定义模块和合约的开发人员。我们也不针对期望在测试语言时就能获得完美开发体验的初学开发者。 42 | 43 | ## 从哪里开始?(Where Do I Start?) 44 | 45 | Begin with understanding [modules and scripts](https://move-language.github.io/move/modules-and-scripts.html) and then work through the [Move Tutorial](https://move-language.github.io/move/creating-coins.html). 46 | 47 | 你可以从了解模块和脚本([modules and scripts](./modules-and-scripts.html))开始,然后跟随Move教程([Move Tutorial](./move-tutorial.html))进行练习。 48 | -------------------------------------------------------------------------------- /src/loops.md: -------------------------------------------------------------------------------- 1 | # While and Loop 2 | 3 | Move offers two constructs for looping: `while` and `loop`. 4 | 5 | Move 提供了两种循环结构: `while` and `loop`. 6 | 7 | ## `while` 循环 8 | 9 | The `while` construct repeats the body (an expression of type unit) until the condition (an expression of type `bool`) evaluates to `false`. 10 | 11 | Here is an example of simple `while` loop that computes the sum of the numbers from `1` to `n`: 12 | 13 | `while` 会重复执行结构(一个 `unit` 类型的表达式), 直到条件语句(`bool` 类型的表达式)运算结果为 `false`。 14 | 15 | 下面是一个简单的 `while` 循环的例子,计算从 `1` 到 `n` 数字之和: 16 | 17 | ```move 18 | fun sum(n: u64): u64 { 19 | let sum = 0; 20 | let i = 1; 21 | while (i <= n) { 22 | sum = sum + i; 23 | i = i + 1 24 | }; 25 | 26 | sum 27 | } 28 | ``` 29 | 30 | Infinite loops are allowed: 31 | 32 | 无限循环是被允许的: 33 | 34 | ```move= 35 | fun foo() { 36 | while (true) { } 37 | } 38 | ``` 39 | 40 | ### `break` 41 | 42 | The `break` expression can be used to exit a loop before the condition evaluates to `false`. For example, this loop uses `break` to find the smallest factor of `n` that's greater than 1: 43 | 44 | `break` 表达式可用于在条件计算结果为 `false` 之前退出循环。例如,这个循环使用 `break` 查找 `n` 大于1的最小因子: 45 | 46 | ```move 47 | fun smallest_factor(n: u64): u64 { 48 | // assuming the input is not 0 or 1 49 | let i = 2; 50 | while (i <= n) { 51 | if (n % i == 0) break; 52 | i = i + 1 53 | }; 54 | 55 | i 56 | } 57 | ``` 58 | 59 | The `break` expression cannot be used outside of a loop. 60 | 61 | `break` 表达式不能在循环之外使用。 62 | 63 | ### `continue` 64 | 65 | The `continue` expression skips the rest of the loop and continues to the next iteration. This loop uses `continue` to compute the sum of `1, 2, ..., n`, except when the number is divisible by 10: 66 | 67 | `continue` 表达式跳过当前循环的剩余部分, 并继续下一轮迭代。下面的例子, 使用 `continue` 去计算 `1, 2, ..., n` 的总和,过滤掉不能被10整除的数: 68 | 69 | ```move 70 | fun sum_intermediate(n: u64): u64 { 71 | let sum = 0; 72 | let i = 0; 73 | while (i < n) { 74 | i = i + 1; 75 | if (i % 10 == 0) continue; 76 | sum = sum + i; 77 | }; 78 | 79 | sum 80 | } 81 | ``` 82 | 83 | The `continue` expression cannot be used outside of a loop. 84 | 85 | `continue` 表达式不能在循环之外使用。 86 | 87 | ### The type of `break` and `continue` 88 | 89 | `break` and `continue`, much like `return` and `abort`, can have any type. The following examples illustrate where this flexible typing can be helpful: 90 | 91 | `break` and `continue`, 和 `return` and `abort` 很相像, 可以是任何类型。下面的例子说明了这种灵活的类型在那些方面有帮助: 92 | 93 | ```move 94 | fun pop_smallest_while_not_equal( 95 | v1: vector, 96 | v2: vector, 97 | ): vector { 98 | let result = vector::empty(); 99 | while (!vector::is_empty(&v1) && !vector::is_empty(&v2)) { 100 | let u1 = *vector::borrow(&v1, vector::length(&v1) - 1); 101 | let u2 = *vector::borrow(&v2, vector::length(&v2) - 1); 102 | let popped = 103 | if (u1 < u2) vector::pop_back(&mut v1) 104 | else if (u2 < u1) vector::pop_back(&mut v2) 105 | else break; // Here, `break` has type `u64` 106 | vector::push_back(&mut result, popped); 107 | }; 108 | 109 | result 110 | } 111 | 112 | fun pick( 113 | indexes: vector, 114 | v1: &vector
, 115 | v2: &vector
116 | ): vector
{ 117 | let len1 = vector::length(v1); 118 | let len2 = vector::length(v2); 119 | let result = vector::empty(); 120 | while (!vector::is_empty(&indexes)) { 121 | let index = vector::pop_back(&mut indexes); 122 | let chosen_vector = 123 | if (index < len1) v1 124 | else if (index < len2) v2 125 | else continue; // Here, `continue` has type `&vector
` 126 | vector::push_back(&mut result, *vector::borrow(chosen_vector, index)) 127 | }; 128 | 129 | result 130 | } 131 | ``` 132 | 133 | ## `loop`表达式 134 | 135 | The `loop` expression repeats the loop body (an expression with type `()`) until it hits a `break` 136 | 137 | Without a `break`, the loop will continue forever 138 | 139 | `loop` 表达式重复循环体(类型为unit()的表达式) ,直到遇到 `break` 为止。 140 | 141 | (下面的代码中)没有 `break`, 循环将一直执行。 142 | 143 | ```move 144 | fun foo() { 145 | let i = 0; 146 | loop { i = i + 1 } 147 | } 148 | ``` 149 | 150 | Here is an example that uses `loop` to write the `sum` function: 151 | 152 | 这是一个使用 `loop` 编写 `sum` 函数的示例(可与 `while` 循环比较): 153 | 154 | ```move 155 | fun sum(n: u64): u64 { 156 | let sum = 0; 157 | let i = 0; 158 | loop { 159 | i = i + 1; 160 | if (i > n) break; 161 | sum = sum + i 162 | }; 163 | 164 | sum 165 | } 166 | ``` 167 | 168 | As you might expect, `continue` can also be used inside a `loop`. Here is `sum_intermediate` from above rewritten using `loop` instead of `while` 169 | 170 | 正如你所料, `continue` 也可以在 `loop` 中使用。这是上面的 `sum_intermediate` 使用 `loop` 代替 `while` 重写的: 171 | 172 | ```move 173 | fun sum_intermediate(n: u64): u64 { 174 | let sum = 0; 175 | let i = 0; 176 | loop { 177 | i = i + 1; 178 | if (i % 10 == 0) continue; 179 | if (i > n) break; 180 | sum = sum + i 181 | }; 182 | 183 | sum 184 | } 185 | ``` 186 | 187 | ## `while` and `loop` 的类型 188 | 189 | Move loops are typed expressions. A `while` expression always has type `()`. 190 | 191 | Move 循环是有类型化的表达式。 `while` 表达式始终具有 `()` 类型。 192 | 193 | ```move 194 | let () = while (i < 10) { i = i + 1 }; 195 | ``` 196 | 197 | If a `loop` contains a `break`, the expression has type unit `()` 198 | 199 | 如果 `loop` 中包含 `break` , 这个表达式的类型则为 unit `()` 200 | 201 | 202 | ```move 203 | (loop { if (i < 10) i = i + 1 else break }: ()); 204 | let () = loop { if (i < 10) i = i + 1 else break }; 205 | ``` 206 | 207 | If `loop` does not have a `break`, `loop` can have any type much like `return`, `abort`, `break`, and `continue`. 208 | 209 | 如果 `loop` 不包含 `break`, `loop` 可以是任何类型, 就像`return`, `abort`, `break`, 和 `continue`。 210 | 211 | ```move 212 | (loop (): u64); 213 | (loop (): address); 214 | (loop (): &vector>); 215 | ``` 216 | -------------------------------------------------------------------------------- /src/modules-and-scripts.md: -------------------------------------------------------------------------------- 1 | # 模块和脚本 (Modules and Scripts) 2 | 3 | Move has two different types of programs: ***Modules*** and ***Scripts***. Modules are libraries that define struct types along with functions that operate on these types. Struct types define the schema of Move's [global storage](./global-storage-structure.md), and module functions define the rules for updating storage. Modules themselves are also stored in global storage. Scripts are executable entrypoints similar to a `main` function in a conventional language. A script typically calls functions of a published module that perform updates to global storage. Scripts are ephemeral code snippets that are not published in global storage. 4 | 5 | A Move source file (or **compilation unit**) may contain multiple modules and scripts. However, publishing a module or executing a script are separate VM operations. 6 | 7 | Move有两种不同类型的程序: ***Modules***和 ***Scripts***。模块(Modules, 相当于智能合约,译者注)是定义结构类型以及对这些类型进行操作的函数的库。结构类型定义Move的[全局存储](./global-storage-structure.md)的模式,模块函数定义更新存储的规则。模块本身也存储在全局存储中。脚本(Scripts)是可执行的入口点,类似于传统语言中的主函数 `main`。脚本通常调用已发布模块的函数来更新全局存储。Scripts是暂时的代码片段,没有发布到全局存储中。 8 | 9 | 一个Move源文件(或**编译单元**)可能包含多个模块和脚本。然而,发布模块或执行脚本都是独立的VM操作。 10 | 11 | ## 语法(Syntax) 12 | 13 | ### 脚本(Scripts) 14 | 15 | A script has the following structure: 16 | script具有以下结构: 17 | 18 | ```text 19 | script { 20 | * 21 | * 22 | fun <[type parameters: constraint]*>([identifier: type]*) 23 | } 24 | ``` 25 | 26 | A `script` block must start with all of its [use](./uses.md) declarations, followed by any [constants](./constants.md) and (finally) the main [function](./functions.md) declaration. The main function can have any name (i.e., it need not be called `main`), is the only function in a script block, can have any number of arguments, and must not return a value. Here is an example with each of these components: 27 | 28 | 一个 `script` 块必须在开头声明[use](./uses.md),然后是[constants](./constants.md)的内容,最后声明主函数 [function](./functions.md)。主函数的名称可以是任意的(也就是说,它不一定命名为 `main`),是script block中唯一的函数,可以有任意数量的参数,并且不能有返回值。下面是示例: 29 | 30 | ```move 31 | script { 32 | // Import the Debug module published at the named account address std. 33 | use std::debug; 34 | 35 | const ONE: u64 = 1; 36 | 37 | fun main(x: u64) { 38 | let sum = x + ONE; 39 | debug::print(&sum) 40 | } 41 | } 42 | ``` 43 | 44 | Scripts have very limited power—they cannot declare friends, struct types or access global storage. Their primary purpose is to invoke module functions. 45 | 46 | 脚本(Scripts) 的功能非常有限—它们不能声明友元、结构类型或访问全局存储, 他们的主要作用主要是调用模块函数. 47 | 48 | ### 模块(Modules) 49 | 50 | Module 具有以下结构: 51 | 52 | ```text 53 | module
:: { 54 | ( | | | | )* 55 | } 56 | ``` 57 | 58 | where `
` is a valid [named or literal address](./chapter_5_address.md). 59 | 60 | 其中 `
` 是一个有效的 [命名或字面量地址](./chapter_5_address.md). 61 | 62 | 例子: 63 | 64 | ```move 65 | module 0x42::Test { 66 | struct Example has copy, drop { i: u64 } 67 | 68 | use std::debug; 69 | friend 0x42::AnotherTest; 70 | 71 | const ONE: u64 = 1; 72 | 73 | public fun print(x: u64) { 74 | let sum = x + ONE; 75 | let example = Example { i: sum }; 76 | debug::print(&sum) 77 | } 78 | } 79 | ``` 80 | 81 | The `module 0x42::Test` part specifies that the module `Test` will be published under the [account address](./chapter_5_address.md) `0x42` in [global storage](./global-storage-structure.md). 82 | 83 | `module 0x42::Test` 这部分指定模块 `Test` 会被发布到[全局存储](./global-storage-structure.md)中[账户地址](./address.md)为 `0x42` 之下. 84 | 85 | Modules can also be declared using [named addresses](./address.md). For example: 86 | 87 | 模块也可以用 [命名地址](./address.md) 来声明,例如: 88 | 89 | ```move 90 | module test_addr::test { 91 | struct Example has copy, drop { a: address} 92 | 93 | use std::debug; 94 | friend test_addr::another_test; 95 | 96 | public fun print() { 97 | let example = Example { a: @test_addr}; 98 | debug::print(&example) 99 | } 100 | } 101 | ``` 102 | 103 | Because named addresses only exist at the source language level and during compilation, named addresses will be fully substituted for their value at the bytecode level. For example if we had the following code: 104 | 105 | 因为命名地址只存在于源码级别,并且在编译期间,命名地址会被转换成字节码。例如,如果我们有下面的代码: 106 | 107 | ```move= 108 | script { 109 | fun example() { 110 | my_addr::m::foo(@my_addr); 111 | } 112 | } 113 | ``` 114 | 115 | and we compiled it with `my_addr` set to `0xC0FFEE`, then it would be equivalent to the following operationally: 116 | 117 | 我们会将 `my_addr` 编译为`0xC0FFEE`,将和下面的代码是等价的: 118 | 119 | ```move= 120 | script { 121 | fun example() { 122 | 0xC0FFEE::m::foo(@0xC0FFEE); 123 | } 124 | } 125 | ``` 126 | 127 | However at the source level, these *are not equivalent*—the function `M::foo` *must* be accessed through the `MyAddr` named address, and not through the numerical value assigned to that address. 128 | 129 | 但是在源码级别,这两个*并不等价* - 函数 `M::foo` 必须通过 `MyAddr`命名地址访问,而不是通过分配给该地址的数值访问。 130 | 131 | Module names can start with letters `a` to `z` or letters `A` to `Z`. After the first character, module names can contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, or digits `0` to `9`. 132 | 133 | 模块名称可以以字母 `a` 到 `z` 或字母 `A` 到 `Z`开头。在第一个字符之后,模块名可以包含下划线`_`、字母 `a` 到 `z` 、字母 `A` 到 `Z` 或数字 `0` 到 `9`。 134 | 135 | ```move 136 | module my_module {} 137 | module foo_bar_42 {} 138 | ``` 139 | 140 | Typically, module names start with an uppercase letter. A module named `my_module` should be stored in a source file named `my_module.move`. 141 | 142 | 通常,模块名称以大写字母开头。名为 `my_module` 的模块应该存储在名为 `my_module.move` 的源文件中。 143 | 144 | All elements inside a `module` block can appear in any order. Fundamentally, a module is a collection of [`types`](./structs-and-resources.md) and [`functions`](./functions.md). [Uses](./uses.md) import types from other modules. [Friends](./friends.md) specify a list of trusted modules. [Constants](./constants.md) define private constants that can be used in the functions of a module. 145 | 146 | `module` 块中的所有元素都可以以任何顺序出现。从根本上说,模块是[`types`](./structs-and-resources.md)和[`functions`](./functions.md)的集合。[Uses](./uses.md)从其他模块导入类型。[Friends](./friends.md)指定一个可信模块列表。[Constants](./constants.md)定义可以在模块函数中使用的私有常量。 -------------------------------------------------------------------------------- /src/references.md: -------------------------------------------------------------------------------- 1 | # 引用(references) 2 | 3 | Move has two types of references: immutable `&` and mutable `&mut`. Immutable references are read 4 | only, and cannot modify the underlying value (or any of its fields). Mutable references allow for 5 | modifications via a write through that reference. Move's type system enforces an ownership 6 | discipline that prevents reference errors. 7 | 8 | Move 支持两种类型的引用:不可变引用 `&` 和可变引用 `&mut`。不可变引用是只读的,不能修改相关值(或其任何字段)。可变引用通过写入该引用进行修改。Move的类型系统强制执行所有权规则,以避免引用错误。 9 | 10 | For more details on the rules of references, see [Structs and Resources](./structs-and-resources.md) 11 | 12 | 更多有关引用规则的详细信息,请参阅:[结构和资源](./structs-and-resources.html). 13 | 14 | ## 引用运算符 (Reference Operators) 15 | 16 | Move provides operators for creating and extending references as well as converting a mutable 17 | reference to an immutable one. Here and elsewhere, we use the notation `e: T` for "expression `e` 18 | has type `T`". 19 | 20 | Move 提供了用于创建和扩展引用以及将可变引用转换为不可变引用的运算符。在这里和其他地方,我们使用符号 `e: T` 来表示“表达式 `e` 的类型是 `T` ” 21 | 22 | | Syntax | Type | Description | 23 | | ----------- | ----------------------------------------------------- | -------------------------------------------------------------- | 24 | | `&e` | `&T` where `e: T` and `T` is a non-reference type | Create an immutable reference to `e` | 25 | | `&mut e` | `&mut T` where `e: T` and `T` is a non-reference type | Create a mutable reference to `e`. | 26 | | `&e.f` | `&T` where `e.f: T` | Create an immutable reference to field `f` of struct `e`. | 27 | | `&mut e.f` | `&mut T` where `e.f: T` | Create a mutable reference to field `f` of struct`e`. | 28 | | `freeze(e)` | `&T` where `e: &mut T` | Convert the mutable reference `e` into an immutable reference. | 29 | 30 | | 语法 | 类型 | 描述 | 31 | | ------ | ------ |------ | 32 | | `&e` | `&T` 其中 `e: T` 和 `T` 是非引用类型 | 创建一个不可变的引用 `e` 33 | | `&mut e` | `&mut T` 其中 `e: T` 和 `T` 是非引用类型 | 创建一个可变的引用 `e` 34 | | `&e.f` | `&T` 其中 `e.f: T` | 创建结构 `e` 的字段 `f` 的不可变引用 35 | | `&mut e.f` | `&mut T` 其中`e.f: T` | 创建结构 `e` 的字段 `f` 的可变引用 36 | | `freeze(e)` | `&T` 其中`e: &mut T` | 将可变引用 `e` 转换为不可变引用 37 | 38 | The `&e.f` and `&mut e.f` operators can be used both to create a new reference into a struct or to extend an existing reference: 39 | 40 | `&e.f`和`&mut e.f`运算符既可以用于在结构中创建新引用,也可以用于扩展现有引用: 41 | 42 | ```move 43 | let s = S { f: 10 }; 44 | let f_ref1: &u64 = &s.f; // works 45 | let s_ref: &S = &s; 46 | let f_ref2: &u64 = &s_ref.f // also works 47 | ``` 48 | 49 | A reference expression with multiple fields works as long as both structs are in the same module: 50 | 51 | 只要两个结构都在同一个模块中,具有多个字段的引用表达式就可以工作: 52 | 53 | ```move 54 | struct A { b: B } 55 | struct B { c : u64 } 56 | fun f(a: &A): &u64 { 57 | &a.b.c 58 | } 59 | ``` 60 | 61 | Finally, note that references to references are not allowed: 62 | 63 | 最后,请注意,不允许引用"引用"(Move不支持多重引用, 但Rust可以,译者注): 64 | 65 | ```move 66 | let x = 7; 67 | let y: &u64 = &x; 68 | let z: &&u64 = &y; // will not compile 69 | ``` 70 | 71 | ## 通过引用进行读写操作 (Reading and Writing Through Reference) 72 | 73 | Both mutable and immutable references can be read to produce a copy of the referenced value. 74 | 75 | Only mutable references can be written. A write `*x = v` discards the value previously stored in `x` 76 | and updates it with `v`. 77 | 78 | 可以读取可变和不可变引用以生成引用值的副本。 79 | 80 | 只能写入可变引用。写入表达式 `*x = v` 会丢弃先前存储在x中的值,并用 `v` 更新。 81 | 82 | Both operations use the C-like `*` syntax. However, note that a read is an expression, whereas a 83 | write is a mutation that must occur on the left hand side of an equals. 84 | 85 | 两种操作都使用类 C `*` 语法。但是请注意,读取是一个表达式,而写入是一个必须发生在等号左侧的改动。 86 | 87 | | Syntax | Type | Description | 88 | | ---------- | ----------------------------------- | ----------------------------------- | 89 | | `*e` | `T` where `e` is `&T` or `&mut T` | Read the value pointed to by `e` | 90 | | `*e1 = e2` | `()` where `e1: &mut T` and `e2: T` | Update the value in `e1` with `e2`. | 91 | 92 | | 语法 | 类型 | 描述 | 93 | | ------ | ------ |------ | 94 | | `&e` | `T` 其中 `e` 为 `&T` 或 `&mut T` | 读取 `e` 所指向的值 95 | | `*e1 = e2` | () 其中 `e1: &mut T` 和 `e2: T` | 用 `e2` 更新 `e1` 中的值 96 | 97 | In order for a reference to be read, the underlying type must have the 98 | [`copy` ability](./abilities.md) as reading the reference creates a new copy of the value. This rule 99 | prevents the copying of resource values: 100 | 101 | 为了读取引用,相关类型必须具备[`copy` 能力](./abilities.html),因为读取引用会创建值的新副本。此规则防止复制资源值: 102 | 103 | ```move= 104 | fun copy_resource_via_ref_bad(c: Coin) { 105 | let c_ref = &c; 106 | let counterfeit: Coin = *c_ref; // not allowed! 107 | pay(c); 108 | pay(counterfeit); 109 | } 110 | ``` 111 | 112 | Dually: in order for a reference to be written to, the underlying type must have the 113 | [`drop` ability](./abilities.md) as writing to the reference will discard (or "drop") the old value. 114 | This rule prevents the destruction of resource values: 115 | 116 | 双重性:为了写入引用,相关类型必须具备[`drop` 能力](./abilities.html),因为写入引用将丢弃(或“删除”)旧值。此规则可防止破坏资源值: 117 | 118 | ```move= 119 | fun destroy_resource_via_ref_bad(ten_coins: Coin, c: Coin) { 120 | let ref = &mut ten_coins; 121 | *ref = c; // not allowed--would destroy 10 coins! 122 | } 123 | ``` 124 | 125 | ## `freeze` 推断 (`freeze` inference) 126 | 127 | A mutable reference can be used in a context where an immutable reference is expected: 128 | 129 | 可变引用可以在预期不可变引用的上下文中使用: 130 | 131 | ```move 132 | let x = 7; 133 | let y: &mut u64 = &mut x; 134 | ``` 135 | 136 | This works because the under the hood, the compiler inserts `freeze` instructions where they are 137 | needed. Here are a few more examples of `freeze` inference in action: 138 | 139 | 这是因为编译器会在底层需要的地方插入 `freeze` 指令。以下是更多 `freeze` 实际推断行为的示例: 140 | 141 | ```move= 142 | fun takes_immut_returns_immut(x: &u64): &u64 { x } 143 | 144 | // freeze inference on return value 145 | fun takes_mut_returns_immut(x: &mut u64): &u64 { x } 146 | 147 | fun expression_examples() { 148 | let x = 0; 149 | let y = 0; 150 | takes_immut_returns_immut(&x); // no inference 151 | takes_immut_returns_immut(&mut x); // inferred freeze(&mut x) 152 | takes_mut_returns_immut(&mut x); // no inference 153 | 154 | assert!(&x == &mut y, 42); // inferred freeze(&mut y) 155 | } 156 | 157 | fun assignment_examples() { 158 | let x = 0; 159 | let y = 0; 160 | let imm_ref: &u64 = &x; 161 | 162 | imm_ref = &x; // no inference 163 | imm_ref = &mut y; // inferred freeze(&mut y) 164 | } 165 | ``` 166 | 167 | ### 子类型化 (Subtyping) 168 | 169 | With this `freeze` inference, the Move type checker can view `&mut T` as a subtype of `&T`. As shown 170 | above, this means that anywhere for any expression where a `&T` value is used, a `&mut T` value can 171 | also be used. This terminology is used in error messages to concisely indicate that a `&mut T` was 172 | needed where a `&T` was supplied. For example 173 | 174 | 通过freeze推断,Move 类型检查器可以将 `&mut T` 视为 `&T` 的子类型。 如上所示,这意味着对于使用 `&T` 值的任何表达式,也可以使用 `&mut T` 值。此术语用于错误消息中,以简明扼要地表明在提供 `&T` 的地方需要 `&mut T` 。例如: 175 | 176 | ```move= 177 | address 0x42 { 178 | module example { 179 | fun read_and_assign(store: &mut u64, new_value: &u64) { 180 | *store = *new_value 181 | } 182 | 183 | fun subtype_examples() { 184 | let x: &u64 = &0; 185 | let y: &mut u64 = &mut 1; 186 | 187 | x = &mut 1; // valid 188 | y = &2; // invalid! 189 | 190 | read_and_assign(y, x); // valid 191 | read_and_assign(x, y); // invalid! 192 | } 193 | } 194 | } 195 | ``` 196 | 197 | will yield the following error messages 198 | 199 | 将产生以下错误消息 200 | 201 | ```text 202 | error: 203 | ┌── example.move:12:9 ─── 204 | │ 205 | 12 │ y = &2; // invalid! 206 | │ ^ Invalid assignment to local 'y' 207 | · 208 | 12 │ y = &2; // invalid! 209 | │ -- The type: '&{integer}' 210 | · 211 | 9 │ let y: &mut u64 = &mut 1; 212 | │ -------- Is not a subtype of: '&mut u64' 213 | │ 214 | 215 | error: 216 | ┌── example.move:15:9 ─── 217 | │ 218 | 15 │ read_and_assign(x, y); // invalid! 219 | │ ^^^^^^^^^^^^^^^^^^^^^ Invalid call of '0x42::example::read_and_assign'. Invalid argument for parameter 'store' 220 | · 221 | 8 │ let x: &u64 = &0; 222 | │ ---- The type: '&u64' 223 | · 224 | 3 │ fun read_and_assign(store: &mut u64, new_value: &u64) { 225 | │ -------- Is not a subtype of: '&mut u64' 226 | │ 227 | ``` 228 | 229 | The only other types currently that has subtyping are [tuples](./tuples.md) 230 | 231 | 当前唯一具有子类型的其他类型是[tuple(元组)](./tuples.html) 232 | 233 | ## 所有权 (Ownership) 234 | 235 | Both mutable and immutable references can always be copied and extended _even if there are existing 236 | copies or extensions of the same reference_: 237 | 238 | _即使同一引用存在现有副本或扩展_,可变引用和不可变引用始终可以被复制和扩展: 239 | 240 | ```move 241 | fun reference_copies(s: &mut S) { 242 | let s_copy1 = s; // ok 243 | let s_extension = &mut s.f; // also ok 244 | let s_copy2 = s; // still ok 245 | ... 246 | } 247 | ``` 248 | 249 | This might be surprising for programmers familiar with Rust's ownership system, which would reject 250 | the code above. Move's type system is more permissive in its treatment of 251 | [copies](./variables.md#move-and-copy), but equally strict in ensuring unique ownership of mutable 252 | references before writes. 253 | 254 | 对于熟悉 Rust 所有权系统的程序员来说,这可能会令人惊讶,因为他们会拒绝上面的代码。Move 的类型系统在处理[副本](./variables.html#move-and-copy)方面更加宽松 ,但在写入前确保可变引用的唯一所有权方面同样严格。 255 | 256 | ### 无法存储引用 (References Cannot Be Stored) 257 | 258 | References and tuples are the _only_ types that cannot be stored as a field value of structs, which 259 | also means that they cannot exist in global storage. All references created during program execution 260 | will be destroyed when a Move program terminates; they are entirely ephemeral. This invariant is 261 | also true for values of types without the `store` [ability](./abilities.md), but note that 262 | references and tuples go a step further by never being allowed in structs in the first place. 263 | 264 | This is another difference between Move and Rust, which allows references to be stored inside of 265 | structs. 266 | 267 | 引用和元组是唯一不能存储为结构的字段值的类型,这也意味着它们不能存在于全局存储中。当 Move 程序终止时,程序执行期间创建的所有引用都将被销毁;它们完全是短暂的。这种不变式也适用于没有[`store` 能力](./chatper_19_abilities.html)的类型的值,但请注意,引用和元组更进一步,从一开始就不允许出现在结构中。 268 | 269 | 这是 Move 和 Rust 之间的另一个区别,后者允许将引用存储在结构内。 270 | 271 | Currently, Move cannot support this because references cannot be 272 | [serialized](https://en.wikipedia.org/wiki/Serialization), but _every Move value must be 273 | serializable_. This requirement comes from Move's 274 | [persistent global storage](./global-storage-structure.md), which needs to serialize values to 275 | persist them across program executions. Structs can be written to global storage, and thus they must 276 | be serializable. 277 | 278 | One could imagine a fancier, more expressive, type system that would allow references to be stored 279 | in structs _and_ ban those structs from existing in global storage. We could perhaps allow 280 | references inside of structs that do not have the `store` [ability](./abilities.md), but that would 281 | not completely solve the problem: Move has a fairly complex system for tracking static reference 282 | safety, and this aspect of the type system would also have to be extended to support storing 283 | references inside of structs. In short, Move's type system (particularly the aspects around 284 | reference safety) would have to expand to support stored references. But it is something we are 285 | keeping an eye on as the language evolves. 286 | 287 | 目前,Move 无法支持这一点,因为引用无法被[序列化](https://en.wikipedia.org/wiki/Serialization),但 _每个 Move 值都必须是可序列化的_。这个要求来自于 Move 的 [持久化全局存储](./global-storage-structure.html),它需要在程序执行期间序列化值以持久化它们。结构体可以写入全局存储,因此它们必须是可序列化的。 288 | 289 | 可以想象一种更奇特、更有表现力的类型系统,它允许将引用存储在结构中,并禁止这些结构存在于全局存储中。我们也许可以允许在没有[`store` 能力](./abilities.html)的结构内部使用引用,但这并不能完全解决问题:Move 有一个相当复杂的系统来跟踪静态引用安全性,并且类型系统的这一方面也必须扩展以支持在结构内部存储引用。简而言之,Move 的类型系统(尤其是与引用安全相关的方面)需要扩展以支持存储的引用。随着语言的发展,我们正在关注这一点。 290 | -------------------------------------------------------------------------------- /src/signer.md: -------------------------------------------------------------------------------- 1 | # 签名者(Signer) 2 | 3 | `signer` is a built-in Move resource type. A `signer` is a [capability](https://en.wikipedia.org/wiki/Object-capability_model) that allows the holder to act on behalf of a particular `address`. 4 | You can think of the native implementation as being: 5 | 6 | `签名者(signer)`是 Move 内置的资源类型。`签名者(signer)`是一种允许持有者代表特定`地址(address)`行使权力的[能力(capability)](https://en.wikipedia.org/wiki/Object-capability_model)。你可以将原生实现(native implementation)视为: 7 | 8 | ```move 9 | struct signer has drop { a: address } 10 | ``` 11 | 12 | A `signer` is somewhat similar to a Unix [UID](https://en.wikipedia.org/wiki/User_identifier) in 13 | that it represents a user authenticated by code _outside_ of Move (e.g., by checking a cryptographic 14 | signature or password). 15 | 16 | `signer` 有点像 Unix [UID](https://en.wikipedia.org/wiki/User_identifier),因为它表示一个通过 Move *之外*的代码(例如,通过检查加密签名或密码)进行身份验证的用户。 17 | 18 | ## 与 `address` 的比较(Comparison to `address`) 19 | 20 | A Move program can create any `address` value without special permission using address literals: 21 | 22 | Move 程序可以使用地址字面量(literal)创建任何`地址(address)`值,而无需特殊许可: 23 | 24 | ```move 25 | let a1 = @0x1; 26 | let a2 = @0x2; 27 | // ... 等等,所有其他可能的地址 28 | ``` 29 | 30 | However, `signer` values are special because they cannot be created via literals or 31 | instructions--only by the Move VM. Before the VM runs a script with parameters of type `signer`, it 32 | will automatically create `signer` values and pass them into the script: 33 | 34 | 但是,`signer` 值是特殊的,因为它们不能通过字面量或者指令创建 —— 只能通过 Move 虚拟机(VM)创建。在虚拟机运行带有 `signer` 类型参数的脚本之前,它会自动创建 `signer` 值并将它们传递给脚本: 35 | 36 | ```move 37 | script { 38 | use std::signer; 39 | fun main(s: signer) { 40 | assert!(signer::address_of(&s) == @0x42, 0); 41 | } 42 | } 43 | ``` 44 | 45 | This script will abort with code `0` if the script is sent from any address other than `0x42`. 46 | 47 | A transaction script can have an arbitrary number of `signer`s as long as the signers are a prefix 48 | to any other arguments. In other words, all of the signer arguments must come first: 49 | 50 | 如果脚本是从 `0x42` 以外的任何地址发送的,则此脚本将中止并返回代码 `0`。 51 | 52 | 交易脚本可以有任意数量的 `signer`,只要 `signer` 参数排在其他参数前面。换句话说,所有 `signer` 参数都必须放在第一位。 53 | 54 | ```move 55 | script { 56 | use std::signer; 57 | fun main(s1: signer, s2: signer, x: u64, y: u8) { 58 | // ... 59 | } 60 | } 61 | ``` 62 | 63 | This is useful for implementing _multi-signer scripts_ that atomically act with the authority of 64 | multiple parties. For example, an extension of the script above could perform an atomic currency 65 | swap between `s1` and `s2`. 66 | 67 | 这对于实现具有多方权限原子行为的*多重签名脚本(multi-signer scripts)*很有用。例如,上述脚本的扩展可以在 `s1` 和 `s2` 之间执行原子货币交换。 68 | 69 | ## `signer` 操作符(`signer` Operators) 70 | 71 | The `std::signer` standard library module provides two utility functions over `signer` values: 72 | 73 | `std::signer` 标准库模块为 `signer` 提供了两个实用函数: 74 | 75 | | 函数 | 描述 | 76 | | ------------------------------------------- | ------------------------------------------------------------- | 77 | | `signer::address_of(&signer): address` | 返回由 `&signer` 包装的地址值。 | 78 | | `signer::borrow_address(&signer): &address` | 返回由 `&signer` 包装的地址的引用。 | 79 | 80 | In addition, the `move_to(&signer, T)` [global storage operator](./global-storage-operators.md) 81 | requires a `&signer` argument to publish a resource `T` under `signer.address`'s account. This 82 | ensures that only an authenticated user can elect to publish a resource under their `address`. 83 | 84 | 此外,`move_to(&signer, T)` [全局存储](./global-storage-operators.md)操作符需要一个 `&signer` 参数在 `signer.address` 的帐户下发布资源 `T`。这确保了只有经过身份验证的用户才能在其地址下发布资源。 85 | 86 | ## 所有权(Ownership) 87 | 88 | Unlike simple scalar values, `signer` values are not copyable, meaning they cannot be copied (from 89 | any operation whether it be through an explicit [`copy`](./variables.md#move-and-copy) instruction 90 | or through a [dereference `*`](./references.md#reference-operators)). 91 | 92 | 与简单的标量值不同,`signer` 值是不可复制的,这意味着他们不能被复制(通过任何操作,无论是通过显式 [`copy`](./variables.md#移动和复制move-and-copy)指令还是通过[解引用(dereference)`*`](./references.md#reference-operators))。 93 | -------------------------------------------------------------------------------- /src/step_1/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /src/step_1/BasicCoin/sources/FirstModule.move: -------------------------------------------------------------------------------- 1 | module 0xCAFE::BasicCoin { 2 | struct Coin has key { 3 | value: u64, 4 | } 5 | 6 | public fun mint(account: signer, value: u64) { 7 | move_to(&account, Coin { value }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/step_2/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [dependencies] 6 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 7 | -------------------------------------------------------------------------------- /src/step_2/BasicCoin/sources/FirstModule.move: -------------------------------------------------------------------------------- 1 | module 0xCAFE::BasicCoin { 2 | // Only included in compilation for testing. Similar to #[cfg(testing)] 3 | // in Rust. Imports the `Signer` module from the MoveStdlib package. 4 | #[test_only] 5 | use std::signer; 6 | 7 | struct Coin has key { 8 | value: u64, 9 | } 10 | 11 | public fun mint(account: signer, value: u64) { 12 | move_to(&account, Coin { value }) 13 | } 14 | 15 | // Declare a unit test. It takes a signer called `account` with an 16 | // address value of `0xC0FFEE`. 17 | #[test(account = @0xC0FFEE)] 18 | fun test_mint_10(account: signer) acquires Coin { 19 | let addr = signer::address_of(&account); 20 | mint(account, 10); 21 | // Make sure there is a `Coin` resource under `addr` with a value of `10`. 22 | // We can access this resource and its value since we are in the 23 | // same module that defined the `Coin` resource. 24 | assert!(borrow_global(addr).value == 10, 0); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/step_2_sol/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_2_sol/BasicCoin/sources/FirstModule.move: -------------------------------------------------------------------------------- 1 | module 0xCAFE::BasicCoin { 2 | // Only included in compilation for testing. Similar to #[cfg(testing)] 3 | // in Rust. 4 | #[test_only] 5 | use std::signer; 6 | 7 | struct Coin has key { 8 | value: u64, 9 | } 10 | 11 | public fun mint(account: signer, value: u64) { 12 | move_to(&account, Coin { value }) 13 | } 14 | 15 | // Declare a unit test. It takes a signer called `account` with an 16 | // address value of `0xC0FFEE`. 17 | #[test(account = @0xC0FFEE)] 18 | fun test_mint_10(account: signer) acquires Coin { 19 | let addr = signer::address_of(&account); 20 | mint(account, 10); 21 | // Make sure there is a `Coin` resource under `addr` with a value of `10`. 22 | // We can access this resource and its value since we are in the 23 | // same module that defined the `Coin` resource. 24 | assert!(borrow_global(addr).value == 10, 0); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/step_2_sol/solution_commands: -------------------------------------------------------------------------------- 1 | # Exercise 1 2 | move test -g 3 | 4 | # Exercise 2 5 | move test --coverage 6 | 7 | Followed by: 8 | 9 | move coverage summary 10 | move coverage summary --summarize-functions 11 | move coverage source --module BasicCoin 12 | -------------------------------------------------------------------------------- /src/step_3/BasicCoin.move: -------------------------------------------------------------------------------- 1 | module NamedAddr::BasicCoin { 2 | struct Coin has store { 3 | value: u64 4 | } 5 | 6 | struct Balance has key { 7 | coin: Coin 8 | } 9 | 10 | /// Publish an empty balance resource under `account`'s address. This function must be called before 11 | /// minting or transferring to the account. 12 | public fun publish_balance(account: &signer) { .. } 13 | 14 | /// Mint `amount` tokens to `mint_addr`. Mint must be approved by the module owner. 15 | public fun mint(module_owner: &signer, mint_addr: address, amount: u64) acquires Balance { .. } 16 | 17 | /// Returns the balance of `owner`. 18 | public fun balance_of(owner: address): u64 acquires Balance { .. } 19 | 20 | /// Transfers `amount` of tokens from `from` to `to`. 21 | public fun transfer(from: &signer, to: address, amount: u64) acquires Balance { .. } 22 | } 23 | -------------------------------------------------------------------------------- /src/step_4/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_4/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Address of the owner of this module 6 | const MODULE_OWNER: address = @NamedAddr; 7 | 8 | /// Error codes 9 | const ENOT_MODULE_OWNER: u64 = 0; 10 | const EINSUFFICIENT_BALANCE: u64 = 1; 11 | const EALREADY_HAS_BALANCE: u64 = 2; 12 | 13 | struct Coin has store { 14 | value: u64 15 | } 16 | 17 | /// Struct representing the balance of each address. 18 | struct Balance has key { 19 | coin: Coin 20 | } 21 | 22 | /// Publish an empty balance resource under `account`'s address. This function must be called before 23 | /// minting or transferring to the account. 24 | public fun publish_balance(account: &signer) { 25 | // TODO: add an assert to check that `account` doesn't already have a `Balance` resource. 26 | let empty_coin = Coin { value: 0 }; 27 | move_to(account, Balance { coin: empty_coin }); 28 | } 29 | 30 | /// Initialize this module. 31 | public fun mint(module_owner: &signer, mint_addr: address, amount: u64) { 32 | // Only the owner of the module can initialize this module 33 | assert!(signer::address_of(module_owner) == MODULE_OWNER, ENOT_MODULE_OWNER); 34 | 35 | // Deposit `amount` of tokens to `mint_addr`'s balance 36 | deposit(mint_addr, Coin { value: amount }); 37 | } 38 | 39 | /// Returns the balance of `owner`. 40 | public fun balance_of(owner: address): u64 acquires Balance { 41 | borrow_global(owner).coin.value 42 | } 43 | 44 | /// Transfers `amount` of tokens from `from` to `to`. 45 | public fun transfer(from: &signer, to: address, amount: u64) acquires Balance { 46 | let check = withdraw(signer::address_of(from), amount); 47 | deposit(to, check); 48 | } 49 | 50 | /// Withdraw `amount` number of tokens from the balance under `addr`. 51 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 52 | let balance = balance_of(addr); 53 | // balance must be greater than the withdraw amount 54 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 55 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 56 | *balance_ref = balance - amount; 57 | Coin { value: amount } 58 | } 59 | 60 | /// Deposit `amount` number of tokens to the balance under `addr`. 61 | fun deposit(_addr: address, check: Coin) { 62 | // TODO: follow the implementation of `withdraw` and implement me! 63 | let Coin { value: _amount } = check; // unpacks the check 64 | 65 | // Get a mutable reference of addr's balance's coin value 66 | 67 | // Increment the value by `amount` 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/step_4_sol/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_4_sol/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Address of the owner of this module 6 | const MODULE_OWNER: address = @NamedAddr; 7 | 8 | /// Error codes 9 | const ENOT_MODULE_OWNER: u64 = 0; 10 | const EINSUFFICIENT_BALANCE: u64 = 1; 11 | const EALREADY_HAS_BALANCE: u64 = 2; 12 | 13 | struct Coin has store { 14 | value: u64 15 | } 16 | 17 | /// Struct representing the balance of each address. 18 | struct Balance has key { 19 | coin: Coin 20 | } 21 | 22 | /// Publish an empty balance resource under `account`'s address. This function must be called before 23 | /// minting or transferring to the account. 24 | public fun publish_balance(account: &signer) { 25 | let empty_coin = Coin { value: 0 }; 26 | assert!(!exists(signer::address_of(account)), EALREADY_HAS_BALANCE); 27 | move_to(account, Balance { coin: empty_coin }); 28 | } 29 | 30 | /// Mint `amount` tokens to `mint_addr`. Mint must be approved by the module owner. 31 | public fun mint(module_owner: &signer, mint_addr: address, amount: u64) acquires Balance { 32 | // Only the owner of the module can initialize this module 33 | assert!(signer::address_of(module_owner) == MODULE_OWNER, ENOT_MODULE_OWNER); 34 | 35 | // Deposit `amount` of tokens to `mint_addr`'s balance 36 | deposit(mint_addr, Coin { value: amount }); 37 | } 38 | 39 | /// Returns the balance of `owner`. 40 | public fun balance_of(owner: address): u64 acquires Balance { 41 | borrow_global(owner).coin.value 42 | } 43 | 44 | /// Transfers `amount` of tokens from `from` to `to`. 45 | public fun transfer(from: &signer, to: address, amount: u64) acquires Balance { 46 | let check = withdraw(signer::address_of(from), amount); 47 | deposit(to, check); 48 | } 49 | 50 | /// Withdraw `amount` number of tokens from the balance under `addr`. 51 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 52 | let balance = balance_of(addr); 53 | // balance must be greater than the withdraw amount 54 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 55 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 56 | *balance_ref = balance - amount; 57 | Coin { value: amount } 58 | } 59 | 60 | /// Deposit `amount` number of tokens to the balance under `addr`. 61 | fun deposit(addr: address, check: Coin) acquires Balance{ 62 | let balance = balance_of(addr); 63 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 64 | let Coin { value } = check; 65 | *balance_ref = balance + value; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/step_5/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_5/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Address of the owner of this module 6 | const MODULE_OWNER: address = @NamedAddr; 7 | 8 | /// Error codes 9 | const ENOT_MODULE_OWNER: u64 = 0; 10 | const EINSUFFICIENT_BALANCE: u64 = 1; 11 | const EALREADY_HAS_BALANCE: u64 = 2; 12 | 13 | struct Coin has store { 14 | value: u64 15 | } 16 | 17 | /// Struct representing the balance of each address. 18 | struct Balance has key { 19 | coin: Coin 20 | } 21 | 22 | /// Publish an empty balance resource under `account`'s address. This function must be called before 23 | /// minting or transferring to the account. 24 | public fun publish_balance(account: &signer) { 25 | let empty_coin = Coin { value: 0 }; 26 | assert!(!exists(signer::address_of(account)), EALREADY_HAS_BALANCE); 27 | move_to(account, Balance { coin: empty_coin }); 28 | } 29 | 30 | /// Mint `amount` tokens to `mint_addr`. Mint must be approved by the module owner. 31 | public fun mint(module_owner: &signer, mint_addr: address, amount: u64) acquires Balance { 32 | // Only the owner of the module can initialize this module 33 | assert!(signer::address_of(module_owner) == MODULE_OWNER, ENOT_MODULE_OWNER); 34 | 35 | // Deposit `amount` of tokens to `mint_addr`'s balance 36 | deposit(mint_addr, Coin { value: amount }); 37 | } 38 | 39 | /// Returns the balance of `owner`. 40 | public fun balance_of(owner: address): u64 acquires Balance { 41 | borrow_global(owner).coin.value 42 | } 43 | 44 | /// Transfers `amount` of tokens from `from` to `to`. 45 | public fun transfer(from: &signer, to: address, amount: u64) acquires Balance { 46 | let check = withdraw(signer::address_of(from), amount); 47 | deposit(to, check); 48 | } 49 | 50 | /// Withdraw `amount` number of tokens from the balance under `addr`. 51 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 52 | let balance = balance_of(addr); 53 | // balance must be greater than the withdraw amount 54 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 55 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 56 | *balance_ref = balance - amount; 57 | Coin { value: amount } 58 | } 59 | 60 | /// Deposit `amount` number of tokens to the balance under `addr`. 61 | fun deposit(addr: address, check: Coin) acquires Balance{ 62 | let balance = balance_of(addr); 63 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 64 | let Coin { value } = check; 65 | *balance_ref = balance + value; 66 | } 67 | 68 | #[test(account = @0x1)] // Creates a signer for the `account` argument with address `@0x1` 69 | #[expected_failure] // This test should abort 70 | fun mint_non_owner(account: signer) acquires Balance { 71 | // Make sure the address we've chosen doesn't match the module 72 | // owner address 73 | publish_balance(&account); 74 | assert!(signer::address_of(&account) != MODULE_OWNER, 0); 75 | mint(&account, @0x1, 10); 76 | } 77 | 78 | #[test(account = @NamedAddr)] // Creates a signer for the `account` argument with the value of the named address `NamedAddr` 79 | fun mint_check_balance(account: signer) acquires Balance { 80 | let addr = signer::address_of(&account); 81 | publish_balance(&account); 82 | mint(&account, @NamedAddr, 42); 83 | assert!(balance_of(addr) == 42, 0); 84 | } 85 | 86 | #[test(account = @0x1)] 87 | fun publish_balance_has_zero(account: signer) acquires Balance { 88 | let addr = signer::address_of(&account); 89 | publish_balance(&account); 90 | assert!(balance_of(addr) == 0, 0); 91 | } 92 | 93 | #[test(account = @0x1)] 94 | #[expected_failure(abort_code = 2)] // Can specify an abort code 95 | fun publish_balance_already_exists(account: signer) { 96 | publish_balance(&account); 97 | publish_balance(&account); 98 | } 99 | 100 | // EXERCISE: Write `balance_of_dne` test here! 101 | 102 | #[test] 103 | #[expected_failure] 104 | fun withdraw_dne() acquires Balance { 105 | // Need to unpack the coin since `Coin` is a resource 106 | Coin { value: _ } = withdraw(@0x1, 0); 107 | } 108 | 109 | #[test(account = @0x1)] 110 | #[expected_failure] // This test should fail 111 | fun withdraw_too_much(account: signer) acquires Balance { 112 | let addr = signer::address_of(&account); 113 | publish_balance(&account); 114 | Coin { value: _ } = withdraw(addr, 1); 115 | } 116 | 117 | #[test(account = @NamedAddr)] 118 | fun can_withdraw_amount(account: signer) acquires Balance { 119 | publish_balance(&account); 120 | let amount = 1000; 121 | let addr = signer::address_of(&account); 122 | mint(&account, addr, amount); 123 | let Coin { value } = withdraw(addr, amount); 124 | assert!(value == amount, 0); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/step_5_sol/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_5_sol/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Address of the owner of this module 6 | const MODULE_OWNER: address = @NamedAddr; 7 | 8 | /// Error codes 9 | const ENOT_MODULE_OWNER: u64 = 0; 10 | const EINSUFFICIENT_BALANCE: u64 = 1; 11 | const EALREADY_HAS_BALANCE: u64 = 2; 12 | 13 | struct Coin has store { 14 | value: u64 15 | } 16 | 17 | /// Struct representing the balance of each address. 18 | struct Balance has key { 19 | coin: Coin 20 | } 21 | 22 | /// Publish an empty balance resource under `account`'s address. This function must be called before 23 | /// minting or transferring to the account. 24 | public fun publish_balance(account: &signer) { 25 | let empty_coin = Coin { value: 0 }; 26 | assert!(!exists(signer::address_of(account)), EALREADY_HAS_BALANCE); 27 | move_to(account, Balance { coin: empty_coin }); 28 | } 29 | 30 | /// Mint `amount` tokens to `mint_addr`. Mint must be approved by the module owner. 31 | public fun mint(module_owner: &signer, mint_addr: address, amount: u64) acquires Balance { 32 | // Only the owner of the module can initialize this module 33 | assert!(signer::address_of(module_owner) == MODULE_OWNER, ENOT_MODULE_OWNER); 34 | 35 | // Deposit `amount` of tokens to `mint_addr`'s balance 36 | deposit(mint_addr, Coin { value: amount }); 37 | } 38 | 39 | /// Returns the balance of `owner`. 40 | public fun balance_of(owner: address): u64 acquires Balance { 41 | borrow_global(owner).coin.value 42 | } 43 | 44 | /// Transfers `amount` of tokens from `from` to `to`. 45 | public fun transfer(from: &signer, to: address, amount: u64) acquires Balance { 46 | let check = withdraw(signer::address_of(from), amount); 47 | deposit(to, check); 48 | } 49 | 50 | /// Withdraw `amount` number of tokens from the balance under `addr`. 51 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 52 | let balance = balance_of(addr); 53 | // balance must be greater than the withdraw amount 54 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 55 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 56 | *balance_ref = balance - amount; 57 | Coin { value: amount } 58 | } 59 | 60 | /// Deposit `amount` number of tokens to the balance under `addr`. 61 | fun deposit(addr: address, check: Coin) acquires Balance{ 62 | let balance = balance_of(addr); 63 | let balance_ref = &mut borrow_global_mut(addr).coin.value; 64 | let Coin { value } = check; 65 | *balance_ref = balance + value; 66 | } 67 | 68 | #[test(account = @0x1)] 69 | #[expected_failure] // This test should abort 70 | fun mint_non_owner(account: signer) acquires Balance { 71 | // Make sure the address we've chosen doesn't match the module 72 | // owner address 73 | publish_balance(&account); 74 | assert!(signer::address_of(&account) != MODULE_OWNER, 0); 75 | mint(&account, @0x1, 10); 76 | } 77 | 78 | #[test(account = @NamedAddr)] 79 | fun mint_check_balance(account: signer) acquires Balance { 80 | let addr = signer::address_of(&account); 81 | publish_balance(&account); 82 | mint(&account, @NamedAddr, 42); 83 | assert!(balance_of(addr) == 42, 0); 84 | } 85 | 86 | #[test(account = @0x1)] 87 | fun publish_balance_has_zero(account: signer) acquires Balance { 88 | let addr = signer::address_of(&account); 89 | publish_balance(&account); 90 | assert!(balance_of(addr) == 0, 0); 91 | } 92 | 93 | #[test(account = @0x1)] 94 | #[expected_failure(abort_code = 2)] // Can specify an abort code 95 | fun publish_balance_already_exists(account: signer) { 96 | publish_balance(&account); 97 | publish_balance(&account); 98 | } 99 | 100 | #[test] 101 | #[expected_failure] 102 | fun balance_of_dne() acquires Balance { 103 | balance_of(@0x1); 104 | } 105 | 106 | #[test] 107 | #[expected_failure] 108 | fun withdraw_dne() acquires Balance { 109 | // Need to unpack the coin since `Coin` is a resource 110 | Coin { value: _ } = withdraw(@0x1, 0); 111 | } 112 | 113 | #[test(account = @0x1)] 114 | #[expected_failure] // This test should fail 115 | fun withdraw_too_much(account: signer) acquires Balance { 116 | let addr = signer::address_of(&account); 117 | publish_balance(&account); 118 | Coin { value: _ } = withdraw(addr, 1); 119 | } 120 | 121 | #[test(account = @NamedAddr)] 122 | fun can_withdraw_amount(account: signer) acquires Balance { 123 | publish_balance(&account); 124 | let amount = 1000; 125 | let addr = signer::address_of(&account); 126 | mint(&account, addr, amount); 127 | let Coin { value } = withdraw(addr, amount); 128 | assert!(value == amount, 0); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/step_6/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_6/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal and generic Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Error codes 6 | const ENOT_MODULE_OWNER: u64 = 0; 7 | const EINSUFFICIENT_BALANCE: u64 = 1; 8 | const EALREADY_HAS_BALANCE: u64 = 2; 9 | 10 | struct Coin has store { 11 | value: u64 12 | } 13 | 14 | struct Balance has key { 15 | coin: Coin 16 | } 17 | 18 | /// Publish an empty balance resource under `account`'s address. This function must be called before 19 | /// minting or transferring to the account. 20 | public fun publish_balance(account: &signer) { 21 | let empty_coin = Coin { value: 0 }; 22 | assert!(!exists>(signer::address_of(account)), EALREADY_HAS_BALANCE); 23 | move_to(account, Balance { coin: empty_coin }); 24 | } 25 | 26 | /// Mint `amount` tokens to `mint_addr`. This method requires a witness with `CoinType` so that the 27 | /// module that owns `CoinType` can decide the minting policy. 28 | public fun mint(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance { 29 | // Deposit `total_value` amount of tokens to mint_addr's balance 30 | deposit(mint_addr, Coin { value: amount }); 31 | } 32 | 33 | public fun balance_of(owner: address): u64 acquires Balance { 34 | borrow_global>(owner).coin.value 35 | } 36 | 37 | /// Transfers `amount` of tokens from `from` to `to`. This method requires a witness with `CoinType` so that the 38 | /// module that owns `CoinType` can decide the transferring policy. 39 | public fun transfer(from: &signer, to: address, amount: u64, _witness: CoinType) acquires Balance { 40 | let check = withdraw(signer::address_of(from), amount); 41 | deposit(to, check); 42 | } 43 | 44 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 45 | let balance = balance_of(addr); 46 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 47 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 48 | *balance_ref = balance - amount; 49 | Coin { value: amount } 50 | } 51 | 52 | fun deposit(addr: address, check: Coin) acquires Balance{ 53 | let balance = balance_of(addr); 54 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 55 | let Coin { value } = check; 56 | *balance_ref = balance + value; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/step_6/BasicCoin/sources/MyOddCoin.move: -------------------------------------------------------------------------------- 1 | /// Module implementing an odd coin, where only odd number of coins can be 2 | /// transferred each time. 3 | module NamedAddr::MyOddCoin { 4 | use std::signer; 5 | use NamedAddr::BasicCoin; 6 | 7 | struct MyOddCoin has drop {} 8 | 9 | const ENOT_ODD: u64 = 0; 10 | 11 | public fun setup_and_mint(account: &signer, amount: u64) { 12 | BasicCoin::publish_balance(account); 13 | BasicCoin::mint(signer::address_of(account), amount, MyOddCoin {}); 14 | } 15 | 16 | public fun transfer(from: &signer, to: address, amount: u64) { 17 | // amount must be odd. 18 | assert!(amount % 2 == 1, ENOT_ODD); 19 | BasicCoin::transfer(from, to, amount, MyOddCoin {}); 20 | } 21 | 22 | /* 23 | Unit tests 24 | */ 25 | #[test(from = @0x42, to = @0x10)] 26 | fun test_odd_success(from: signer, to: signer) { 27 | setup_and_mint(&from, 42); 28 | setup_and_mint(&to, 10); 29 | 30 | // transfer an odd number of coins so this should succeed. 31 | transfer(&from, @0x10, 7); 32 | 33 | assert!(BasicCoin::balance_of(@0x42) == 35, 0); 34 | assert!(BasicCoin::balance_of(@0x10) == 17, 0); 35 | } 36 | 37 | #[test(from = @0x42, to = @0x10)] 38 | #[expected_failure] 39 | fun test_not_odd_failure(from: signer, to: signer) { 40 | setup_and_mint(&from, 42); 41 | setup_and_mint(&to, 10); 42 | 43 | // transfer an even number of coins so this should fail. 44 | transfer(&from, @0x10, 8); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/step_7/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_7/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal and generic Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Error codes 6 | const ENOT_MODULE_OWNER: u64 = 0; 7 | const EINSUFFICIENT_BALANCE: u64 = 1; 8 | const EALREADY_HAS_BALANCE: u64 = 2; 9 | 10 | struct Coin has store { 11 | value: u64 12 | } 13 | 14 | struct Balance has key { 15 | coin: Coin 16 | } 17 | 18 | /// Publish an empty balance resource under `account`'s address. This function must be called before 19 | /// minting or transferring to the account. 20 | public fun publish_balance(account: &signer) { 21 | let empty_coin = Coin { value: 0 }; 22 | assert!(!exists>(signer::address_of(account)), EALREADY_HAS_BALANCE); 23 | move_to(account, Balance { coin: empty_coin }); 24 | } 25 | 26 | /// Mint `amount` tokens to `mint_addr`. This method requires a witness with `CoinType` so that the 27 | /// module that owns `CoinType` can decide the minting policy. 28 | public fun mint(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance { 29 | // Deposit `total_value` amount of tokens to mint_addr's balance 30 | deposit(mint_addr, Coin { value: amount }); 31 | } 32 | 33 | public fun balance_of(owner: address): u64 acquires Balance { 34 | borrow_global>(owner).coin.value 35 | } 36 | 37 | spec balance_of { 38 | pragma aborts_if_is_strict; 39 | } 40 | 41 | /// Transfers `amount` of tokens from `from` to `to`. This method requires a witness with `CoinType` so that the 42 | /// module that owns `CoinType` can decide the transferring policy. 43 | public fun transfer(from: &signer, to: address, amount: u64, _witness: CoinType) acquires Balance { 44 | let check = withdraw(signer::address_of(from), amount); 45 | deposit(to, check); 46 | } 47 | 48 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 49 | let balance = balance_of(addr); 50 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 51 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 52 | *balance_ref = balance - amount; 53 | Coin { value: amount } 54 | } 55 | 56 | fun deposit(addr: address, check: Coin) acquires Balance{ 57 | let balance = balance_of(addr); 58 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 59 | let Coin { value } = check; 60 | *balance_ref = balance + value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/step_8/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xCAFE" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_8/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal and generic Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Error codes 6 | const ENOT_MODULE_OWNER: u64 = 0; 7 | const EINSUFFICIENT_BALANCE: u64 = 1; 8 | const EALREADY_HAS_BALANCE: u64 = 2; 9 | const EEQUAL_ADDR: u64 = 4; 10 | 11 | struct Coin has store { 12 | value: u64 13 | } 14 | 15 | struct Balance has key { 16 | coin: Coin 17 | } 18 | 19 | /// Publish an empty balance resource under `account`'s address. This function must be called before 20 | /// minting or transferring to the account. 21 | public fun publish_balance(account: &signer) { 22 | let empty_coin = Coin { value: 0 }; 23 | assert!(!exists>(signer::address_of(account)), EALREADY_HAS_BALANCE); 24 | move_to(account, Balance { coin: empty_coin }); 25 | } 26 | 27 | /// Mint `amount` tokens to `mint_addr`. This method requires a witness with `CoinType` so that the 28 | /// module that owns `CoinType` can decide the minting policy. 29 | public fun mint(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance { 30 | // Deposit `total_value` amount of tokens to mint_addr's balance 31 | deposit(mint_addr, Coin { value: amount }); 32 | } 33 | 34 | public fun balance_of(owner: address): u64 acquires Balance { 35 | borrow_global>(owner).coin.value 36 | } 37 | 38 | spec balance_of { 39 | pragma aborts_if_is_strict; 40 | aborts_if !exists>(owner); 41 | } 42 | 43 | /// Transfers `amount` of tokens from `from` to `to`. This method requires a witness with `CoinType` so that the 44 | /// module that owns `CoinType` can decide the transferring policy. 45 | public fun transfer(from: &signer, to: address, amount: u64, _witness: CoinType) acquires Balance { 46 | let from_addr = signer::address_of(from); 47 | assert!(from_addr != to, EEQUAL_ADDR); 48 | let check = withdraw(from_addr, amount); 49 | deposit(to, check); 50 | } 51 | 52 | spec transfer { 53 | let addr_from = signer::address_of(from); 54 | 55 | let balance_from = global>(addr_from).coin.value; 56 | let balance_to = global>(to).coin.value; 57 | let post balance_from_post = global>(addr_from).coin.value; 58 | let post balance_to_post = global>(to).coin.value; 59 | 60 | ensures balance_from_post == balance_from - amount; 61 | ensures balance_to_post == balance_to + amount; 62 | } 63 | 64 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 65 | let balance = balance_of(addr); 66 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 67 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 68 | *balance_ref = balance - amount; 69 | Coin { value: amount } 70 | } 71 | 72 | spec withdraw { 73 | let balance = global>(addr).coin.value; 74 | 75 | aborts_if !exists>(addr); 76 | aborts_if balance < amount; 77 | 78 | let post balance_post = global>(addr).coin.value; 79 | ensures result == Coin { value: amount }; 80 | ensures balance_post == balance - amount; 81 | } 82 | 83 | fun deposit(addr: address, check: Coin) acquires Balance{ 84 | let balance = balance_of(addr); 85 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 86 | let Coin { value } = check; 87 | *balance_ref = balance + value; 88 | } 89 | 90 | spec deposit { 91 | let balance = global>(addr).coin.value; 92 | let check_value = check.value; 93 | 94 | aborts_if !exists>(addr); 95 | aborts_if balance + check_value > MAX_U64; 96 | 97 | let post balance_post = global>(addr).coin.value; 98 | ensures balance_post == balance + check_value; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/step_8_sol/BasicCoin/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "BasicCoin" 3 | version = "0.0.0" 4 | 5 | [addresses] 6 | NamedAddr = "0xDEADBEEF" 7 | 8 | [dependencies] 9 | MoveStdlib = { local = "../../../../move-stdlib/", addr_subst = { "std" = "0x1" } } 10 | -------------------------------------------------------------------------------- /src/step_8_sol/BasicCoin/sources/BasicCoin.move: -------------------------------------------------------------------------------- 1 | /// This module defines a minimal and generic Coin and Balance. 2 | module NamedAddr::BasicCoin { 3 | use std::signer; 4 | 5 | /// Error codes 6 | const ENOT_MODULE_OWNER: u64 = 0; 7 | const EINSUFFICIENT_BALANCE: u64 = 1; 8 | const EALREADY_HAS_BALANCE: u64 = 2; 9 | const EALREADY_INITIALIZED: u64 = 3; 10 | const EEQUAL_ADDR: u64 = 4; 11 | 12 | struct Coin has store { 13 | value: u64 14 | } 15 | 16 | struct Balance has key { 17 | coin: Coin 18 | } 19 | 20 | public fun publish_balance(account: &signer) { 21 | let empty_coin = Coin { value: 0 }; 22 | assert!(!exists>(signer::address_of(account)), EALREADY_HAS_BALANCE); 23 | move_to(account, Balance { coin: empty_coin }); 24 | } 25 | 26 | spec publish_balance { 27 | include Schema_publish {addr: signer::address_of(account), amount: 0}; 28 | } 29 | 30 | spec schema Schema_publish { 31 | addr: address; 32 | amount: u64; 33 | 34 | aborts_if exists>(addr); 35 | 36 | ensures exists>(addr); 37 | let post balance_post = global>(addr).coin.value; 38 | 39 | ensures balance_post == amount; 40 | } 41 | 42 | /// Mint `amount` tokens to `mint_addr`. This method requires a witness with `CoinType` so that the 43 | /// module that owns `CoinType` can decide the minting policy. 44 | public fun mint(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance { 45 | // Deposit `total_value` amount of tokens to mint_addr's balance 46 | deposit(mint_addr, Coin { value: amount }); 47 | } 48 | 49 | spec mint { 50 | include DepositSchema {addr: mint_addr, amount}; 51 | } 52 | 53 | public fun balance_of(owner: address): u64 acquires Balance { 54 | borrow_global>(owner).coin.value 55 | } 56 | 57 | spec balance_of { 58 | pragma aborts_if_is_strict; 59 | aborts_if !exists>(owner); 60 | } 61 | 62 | /// Transfers `amount` of tokens from `from` to `to`. This method requires a witness with `CoinType` so that the 63 | /// module that owns `CoinType` can decide the transferring policy. 64 | public fun transfer(from: &signer, to: address, amount: u64, _witness: CoinType) acquires Balance { 65 | let from_addr = signer::address_of(from); 66 | assert!(from_addr != to, EEQUAL_ADDR); 67 | let check = withdraw(from_addr, amount); 68 | deposit(to, check); 69 | } 70 | 71 | spec transfer { 72 | let addr_from = signer::address_of(from); 73 | 74 | let balance_from = global>(addr_from).coin.value; 75 | let balance_to = global>(to).coin.value; 76 | let post balance_from_post = global>(addr_from).coin.value; 77 | let post balance_to_post = global>(to).coin.value; 78 | 79 | aborts_if !exists>(addr_from); 80 | aborts_if !exists>(to); 81 | aborts_if balance_from < amount; 82 | aborts_if balance_to + amount > MAX_U64; 83 | aborts_if addr_from == to; 84 | 85 | ensures balance_from_post == balance_from - amount; 86 | ensures balance_to_post == balance_to + amount; 87 | } 88 | 89 | fun withdraw(addr: address, amount: u64) : Coin acquires Balance { 90 | let balance = balance_of(addr); 91 | assert!(balance >= amount, EINSUFFICIENT_BALANCE); 92 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 93 | *balance_ref = balance - amount; 94 | Coin { value: amount } 95 | } 96 | 97 | spec withdraw { 98 | let balance = global>(addr).coin.value; 99 | 100 | aborts_if !exists>(addr); 101 | aborts_if balance < amount; 102 | 103 | let post balance_post = global>(addr).coin.value; 104 | ensures result == Coin { value: amount }; 105 | ensures balance_post == balance - amount; 106 | } 107 | 108 | fun deposit(addr: address, check: Coin) acquires Balance{ 109 | let balance = balance_of(addr); 110 | let balance_ref = &mut borrow_global_mut>(addr).coin.value; 111 | let Coin { value } = check; 112 | *balance_ref = balance + value; 113 | } 114 | 115 | spec deposit { 116 | let balance = global>(addr).coin.value; 117 | let check_value = check.value; 118 | 119 | aborts_if !exists>(addr); 120 | aborts_if balance + check_value > MAX_U64; 121 | 122 | let post balance_post = global>(addr).coin.value; 123 | ensures balance_post == balance + check_value; 124 | } 125 | 126 | spec schema DepositSchema { 127 | addr: address; 128 | amount: u64; 129 | let balance = global>(addr).coin.value; 130 | 131 | aborts_if !exists>(addr); 132 | aborts_if balance + amount > MAX_U64; 133 | 134 | let post balance_post = global>(addr).coin.value; 135 | ensures balance_post == balance + amount; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/tuples.md: -------------------------------------------------------------------------------- 1 | # 元组和Unit (Tuples and Unit) 2 | 3 | Move does not fully support tuples as one might expect coming from another language with them as a 4 | first-class value. However, in order to support multiple return values, Move has tuple-like 5 | expressions. These expressions do not result in a concrete value at runtime (there are no tuples in 6 | the bytecode), and as a result they are very limited: they can only appear in expressions (usually 7 | in the return position for a function); they cannot be bound to local variables; they cannot be 8 | stored in structs; and tuple types cannot be used to instantiate generics. 9 | 10 | Move并不完全支持元组,因为人们可能期望来自另一种语言的元组将它们作为一等值。然而,为了支持多个返回值,Move具有类似元组的表达式。这些表达式在运行时不会产生具体的值(字节码中没有元组),因此它们非常有限:它们只能出现在表达式中(通常在函数的返回位置);它们不能绑定到局部变量;它们不能存储在结构中;元组类型不能用于实例化泛型。 11 | 12 | Similarly, unit `()` is a type created by the Move source language in order to be expression based. 13 | The unit value `()` does not result in any runtime value. We can consider unit`()` to be an empty 14 | tuple, and any restrictions that apply to tuples also apply to unit. 15 | 16 | 类似地,为了基于表达式,unit `()` 是由Move源语言创建的一种类型。unit的值 `()` 不会产生任何运行值。我们可以将 unit `()` 视为空元组,适用于元组的任何限制也适用于 `unit` 。 17 | 18 | It might feel weird to have tuples in the language at all given these restrictions. But one of the 19 | most common use cases for tuples in other languages is for functions to allow functions to return 20 | multiple values. Some languages work around this by forcing the users to write structs that contain 21 | the multiple return values. However in Move, you cannot put references inside of 22 | [structs](./structs-and-resources.md). This required Move to support multiple return values. These 23 | multiple return values are all pushed on the stack at the bytecode level. At the source level, these 24 | multiple return values are represented using tuples. 25 | 26 | 考虑到这些限制,在语言中使用元组可能会感觉很奇怪。但在其他语言中,元组最常见的用例之一是允许函数返回多个值。一些语言通过强制用户编写包含多个返回值的结构来解决这个问题。然而,在Move中,不能将引用放在[结构体](./structs-and-resources.html)内部。这需要Move支持多个返回值。这些多个返回值都在字节码级别压入到堆栈中。在源代码级别,这些多个返回值使用元组表示。 27 | 28 | ## 字面量 (Literals) 29 | 30 | Tuples are created by a comma separated list of expressions inside of parentheses 31 | 32 | | Syntax | Type | Description | 33 | | --------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------ | 34 | | `()` | `(): ()` | Unit, the empty tuple, or the tuple of arity 0 | 35 | | `(e1, ..., en)` | `(e1, ..., en): (T1, ..., Tn)` where `e_i: Ti` s.t. `0 < i <= n` and `n > 0` | A `n`-tuple, a tuple of arity `n`, a tuple with `n` elements | 36 | 37 | 元组是由括号内以逗号分隔的表达式列表创建的 38 | | 语法 | 类型 | 描述 | 39 | | ------ | ------ | ------ | 40 | | `()` | `(): ()` | Unit、空元组 或 0元素元组 41 | | `(e1, ..., en)` | `(e1, ..., en): (T1, ..., Tn)` 其中 `e_i: Ti s.t. 0 < i <= n` and `n > 0` |带有n个元素的元组 42 | 43 | Note that `(e)` does not have type `(e): (t)`, in other words there is no tuple with one element. If 44 | there is only a single element inside of the parentheses, the parentheses are only used for 45 | disambiguation and do not carry any other special meaning. 46 | 47 | Sometimes, tuples with two elements are called "pairs" and tuples with three elements are called 48 | "triples." 49 | 50 | 请注意,`(e)`没有类型 `(e): (t)`,换句话说,没有一个元素的元组。如果括号内只有一个元素,则括号仅用于消除歧义,不带有任何其他特殊意义。 51 | 52 | 有时,具有两个元素的元组称为 “对偶”,而具有三个元素的元组称为“三元组”。 53 | 54 | ### 例子 55 | 56 | ```move= 57 | address 0x42 { 58 | module example { 59 | // all 3 of these functions are equivalent 60 | 61 | // when no return type is provided, it is assumed to be `()` 62 | fun returs_unit_1() { } 63 | 64 | // there is an implicit () value in empty expression blocks 65 | fun returs_unit_2(): () { } 66 | 67 | // explicit version of `returs_unit_1` and `returs_unit_2` 68 | fun returs_unit_3(): () { () } 69 | 70 | fun returns_3_values(): (u64, bool, address) { 71 | (0, false, @0x42) 72 | } 73 | fun returns_4_values(x: &u64): (&u64, u8, u128, vector) { 74 | (x, 0, 1, b"foobar") 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | ## 操作 (Operations) 81 | 82 | The only operation that can be done on tuples currently is destructuring. 83 | 84 | 目前唯一可以对元组执行的操作是解构。 85 | 86 | ### 解构 (Destructuring) 87 | 88 | For tuples of any size, they can be destructured in either a `let` binding or in an assignment. 89 | 90 | For example: 91 | 92 | 对于任何大小的元组,它们可以在 `let` 绑定或赋值中被解构。 93 | 94 | 例如: 95 | 96 | ```move= 97 | address 0x42 { 98 | module example { 99 | // all 3 of these functions are equivalent 100 | fun returns_unit() {} 101 | fun returns_2_values(): (bool, bool) { (true, false) } 102 | fun returns_4_values(x: &u64): (&u64, u8, u128, vector) { (x, 0, 1, b"foobar") } 103 | 104 | fun examples(cond: bool) { 105 | let () = (); 106 | let (x, y): (u8, u64) = (0, 1); 107 | let (a, b, c, d) = (@0x0, 0, false, b""); 108 | 109 | () = (); 110 | (x, y) = if (cond) (1, 2) else (3, 4); 111 | (a, b, c, d) = (@0x1, 1, true, b"1"); 112 | } 113 | 114 | fun examples_with_function_calls() { 115 | let () = returns_unit(); 116 | let (x, y): (bool, bool) = returns_2_values(); 117 | let (a, b, c, d) = returns_4_values(&0); 118 | 119 | () = returns_unit(); 120 | (x, y) = returns_2_values(); 121 | (a, b, c, d) = returns_4_values(&1); 122 | } 123 | } 124 | } 125 | ``` 126 | 127 | For more details, see [Move Variables](./variables.md). 128 | 129 | 详情可参阅 [变量](./variables.md). 130 | 131 | ## 子类型化 (Subtyping) 132 | 133 | Along with references, tuples are the only types that have subtyping in Move. Tuples do have 134 | subtyping only in the sense that subtype with references (in a covariant way). 135 | 136 | 除了引用之外,元组是唯一在Move中具有子类型的类型。元组只有在子类型具有引用的意义上才具有子类型(以协变方式)。 137 | 138 | 例如: 139 | 140 | ```move= 141 | let x: &u64 = &0; 142 | let y: &mut u64 = &mut 1; 143 | 144 | // (&u64, &mut u64) is a subtype of (&u64, &u64) 145 | // since &mut u64 is a subtype of &u64 146 | let (a, b): (&u64, &u64) = (x, y); 147 | // (&mut u64, &mut u64) is a subtype of (&u64, &u64) 148 | // since &mut u64 is a subtype of &u64 149 | let (c, d): (&u64, &u64) = (y, y); 150 | // error! (&u64, &mut u64) is NOT a subtype of (&mut u64, &mut u64) 151 | // since &u64 is NOT a subtype of &mut u64 152 | let (e, f): (&mut u64, &mut u64) = (x, y); 153 | ``` 154 | 155 | ## 所有权 (Ownership) 156 | 157 | As mentioned above, tuple values don't really exist at runtime. And currently they cannot be stored 158 | into local variables because of this (but it is likely that this feature will come soon). As such, 159 | tuples can only be moved currently, as copying them would require putting them into a local variable 160 | first. 161 | 162 | 如上所述,元组值在运行时并不真正存在。由于这个原因,目前它们不能存储到局部变量中(但这个功能很可能很快就会出现)。因此,元组目前只能移动,因为复制它们需要先将它们放入局部变量中。 163 | -------------------------------------------------------------------------------- /src/unit-testing.md: -------------------------------------------------------------------------------- 1 | # 单元测试 (Unit Tests) 2 | 3 | Unit testing for Move adds three new annotations to the Move source language: 4 | 5 | Move 语言中存在三种单元测试标注: 6 | 7 | * `#[test]` 8 | * `#[test_only]`, and 9 | * `#[expected_failure]`. 10 | 11 | They respectively mark a function as a test, mark a module or module member (`use`, function, or struct) as code to be included for testing only, and mark that a test is expected to fail. These annotations can be placed on a function with any visibility. Whenever a module or module member is annotated as `#[test_only]` or `#[test]`, it will not be included in the compiled bytecode unless it is compiled for testing. 12 | 13 | 它们分别把函数、模块或模块成员(`use` 声明,函数 function,或结构体 struct)标记为只用于测试的代码,同时也标记期望失败的测试。这些标注可以用在任何可见性(visibility)函数上。无论何种情况,被标注为 `#[test_only]` 或 `#[test]` 的模块或模块成员除非用于测试,其它情况都不会被编译成字节码。 14 | 15 | ## 测试注解:含义和使用方法(Testing Annotations: Their Meaning and Usage) 16 | 17 | Both the `#[test]` and `#[expected_failure]` annotations can be used either with or without arguments. 18 | 19 | `#[test]` 和 `#[expected_failure]` 两个注解均可以在有、无参数情况下使用。 20 | 21 | Without arguments, the `#[test]` annotation can only be placed on a function with no parameters. This annotation simply marks this function as a test to be run by the unit testing harness. 22 | 23 | 没有参数的 `#[test]` 标记只能用于没有参数的函数。表示该函数作为单元测试函数被运行。 24 | 25 | ``` 26 | #[test] // 正确 // OK 27 | fun this_is_a_test() { ... } 28 | 29 | #[test] // 编译失败,因为函数需要参数 // Will fail to compile since the test takes an argument 30 | fun this_is_not_correct(arg: signer) { ... } 31 | ``` 32 | 33 | A test can also be annotated as an `#[expected_failure]`. This annotation marks that the test should is expected to raise an error. You can ensure that a test is aborting with a specific abort code by annotating it with `#[expected_failure(abort_code = )]`, if it then fails with a different abort code or with a non-abort error the test will fail. Only functions that have the `#[test]` annotation can also be annotated as an #`[expected_failure]`. 34 | 35 | 测试也可以使用 `#[expected_failure]` 标注,表示该函数会抛出错误。你可以使用 `#[expected_failure(abort_code = )]` 这种方式方式确保此测试会被指定错误码打断,如果抛出不同错误码或没有抛出错误测试将失败。只有被 `#[test]` 标注的函数才能使用 `#[expected_failure]` 标注。 36 | 37 | ``` 38 | #[test] 39 | #[expected_failure] 40 | public fun this_test_will_abort_and_pass() { abort 1 } 41 | 42 | #[test] 43 | #[expected_failure] 44 | public fun test_will_error_and_pass() { 1/0; } 45 | 46 | #[test] 47 | #[expected_failure(abort_code = 0)] 48 | public fun test_will_error_and_fail() { 1/0; } 49 | 50 | #[test, expected_failure] // 可以合并多个属性。测试将会通过。 // Can have multiple in one attribute. This test will pass. 51 | public fun this_other_test_will_abort_and_pass() { abort 1 } 52 | ``` 53 | 54 | With arguments, a test annotation takes the form `#[test( =
, ..., =
)]`. If a function is annotated in such a manner, the function's parameters must be a permutation of the parameters <`param_name_1>, ..., `, i.e., the order of these parameters as they occur in the function and their order in the test annotation do not have to be the same, but they must be able to be matched up with each other by name. 55 | 56 | 测试标注可以采用 `#[test( =
, ..., =
)]` 这种形式指定参数。如果函数使用这样的标注,函数的参数则必须为 `, ..., ` 的形式。参数在函数中的顺序不必与注解中顺序一致,但必须要能根据参数名匹配。 57 | 58 | Only parameters with a type of `signer` are supported as test parameters. If a non-`signer` parameter is supplied, the test will result in an error when run. 59 | 60 | 只有 `signer` 类型可以用作测试参数。使用非 `signer` 类型参数,测试将会失败。 61 | 62 | ``` 63 | #[test(arg = @0xC0FFEE)] // 正确 // OK 64 | fun this_is_correct_now(arg: signer) { ... } 65 | 66 | #[test(wrong_arg_name = @0xC0FFEE)] // 不正确: 参数名不匹配 // Not correct: arg name doesn't match 67 | fun this_is_incorrect(arg: signer) { ... } 68 | 69 | #[test(a = @0xC0FFEE, b = @0xCAFE)] // 正确,多参数情况下必须为每个参数提供值。 // OK. We support multiple signer arguments, but you must always provide a value for that argument 70 | fun this_works(a: signer, b: signer) { ... } 71 | 72 | // 在某处声明一个命名地址(named address) // somewhere a named address is declared 73 | #[test_only] // 命名地址支持 test-only 注解 // test-only named addresses are supported 74 | address TEST_NAMED_ADDR = @0x1; 75 | ... 76 | #[test(arg = @TEST_NAMED_ADDR)] // 支持命名地址! // Named addresses are supported! 77 | fun this_is_correct_now(arg: signer) { ... } 78 | ``` 79 | 80 | An expected failure annotation can also take the form `#[expected_failure(abort_code = )]`. If a test function is annotated in such a way, the test must abort with an abort code equal to ``. Any other failure or abort code will result in a test failure. 81 | 82 | 预期失败的标注使用 `#[expected_failure(abort_code = )]` 这种形式。如果函数被这样标注,测试错误码必须为 ``。任何其它的错误或错误码都会失败。 83 | 84 | ``` 85 | #[test, expected_failure(abort_code = 1)] // 这个测试会失败 // This test will fail 86 | fun this_test_should_abort_and_fail() { abort 0 } 87 | 88 | #[test] 89 | #[expected_failure(abort_code = 0)] // 这个测试会通过 // This test will pass 90 | fun this_test_should_abort_and_pass_too() { abort 0 } 91 | ``` 92 | 93 | A module and any of its members can be declared as test only. In such a case the item will only be included in the compiled Move bytecode when compiled in test mode. Additionally, when compiled outside of test mode, any non-test `use`s of a `#[test_only]` module will raise an error during compilation. 94 | 95 | 模块和它的成员可以被声明为仅测试用。这种情况它们只会在测试模式下编译。此外,在非测试模式下,任何被 `#[test_only]` 标记的模块都会在编译时报错。 96 | 97 | ``` 98 | #[test_only] // test only 属性可以用于模块 // test only attributes can be attached to modules 99 | module abc { ... } 100 | 101 | #[test_only] // test only 属性可以用于命名地址 // test only attributes can be attached to named addresses 102 | address ADDR = @0x1; 103 | 104 | #[test_only] // .. 用于 use 声明 // .. to uses 105 | use 0x1::some_other_module; 106 | 107 | #[test_only] // .. 用于结构体 // .. to structs 108 | struct SomeStruct { ... } 109 | 110 | #[test_only] // .. 用于函数。只能在测试函数中调用,但自身不是测试 // .. and functions. Can only be called from test code, but not a test 111 | fun test_only_function(...) { ... } 112 | ``` 113 | 114 | ## 运行单元测试(Running Unit Tests) 115 | 116 | Unit tests for a Move package can be run with the [`move test` 117 | command](./packages.md). 118 | 119 | 使用 [`move test` 命令](./packages.md)运行包中的单元测试。 120 | 121 | When running tests, every test will either `PASS`, `FAIL`, or `TIMEOUT`. If a test case fails, the location of the failure along with the function name that caused the failure will be reported if possible. You can see an example of this below. 122 | 123 | 运行测试的结果包括 `PASS`、`FAIL` 或 `TIMEOUT`。如果测试失败,将会尽可能的提供执行失败的位置及函数名信息。请看下面的例子。 124 | 125 | A test will be marked as timing out if it exceeds the maximum number of instructions that can be executed for any single test. This bound can be changed using the options below, and its default value is set to 5000 instructions. Additionally, while the result of a test is always deterministic, tests are run in parallel by default, so the ordering of test results in a test run is non-deterministic unless running with only one thread (see `OPTIONS` below). 126 | 127 | 任何测试执行超过最大数量指令限制将会标记成超时。可以通过参数调整此限制,默认值为 5000 条指令。此外,虽然测试结果是确定的,但由于测试默认并行执行,所以测试结果的顺序是不确定的,除非使用单线程模式(见下述参数)。 128 | 129 | There are also a number of options that can be passed to the unit testing binary to fine-tune testing and to help debug failing tests. These can be found using the the help flag: 130 | 131 | 存在大量参数细粒度调整测试工具的行为,帮助调试失败的测试。可以通过 help 参数查看。 132 | 133 | ``` 134 | $ move -h 135 | ``` 136 | 137 | ## 示例(Example) 138 | 139 | A simple module using some of the unit testing features is shown in the following example: 140 | 141 | 下面例子展示了一个简单的使用了单元测试特性的模块: 142 | 143 | First create an empty package and change directory into it: 144 | 145 | 首先创建一个空 package 进入目录: 146 | 147 | ``` 148 | $ move new TestExample; cd TestExample 149 | ``` 150 | 151 | Next add the following to the `Move.toml`: 152 | 153 | 接下来添加下面内容到 `Move.toml` 文件: 154 | 155 | ``` 156 | [dependencies] 157 | MoveStdlib = { git = "https://github.com/diem/diem.git", subdir="language/move-stdlib", rev = "56ab033cc403b489e891424a629e76f643d4fb6b", addr_subst = { "std" = "0x1" } } 158 | ``` 159 | 160 | Next add the following module under the `sources` directory: 161 | 162 | 接下来在 `sources` 目录下添加下述模块: 163 | 164 | ``` 165 | // 文件路径: sources/my_module.move // filename: sources/my_module.move 166 | module 0x1::my_module { 167 | 168 | struct MyCoin has key { value: u64 } 169 | 170 | public fun make_sure_non_zero_coin(coin: MyCoin): MyCoin { 171 | assert!(coin.value > 0, 0); 172 | coin 173 | } 174 | 175 | public fun has_coin(addr: address): bool { 176 | exists(addr) 177 | } 178 | 179 | #[test] 180 | fun make_sure_non_zero_coin_passes() { 181 | let coin = MyCoin { value: 1 }; 182 | let MyCoin { value: _ } = make_sure_non_zero_coin(coin); 183 | } 184 | 185 | #[test] 186 | // 如果不关心错误码也可以使用 #[expected_failure] // Or #[expected_failure] if we don't care about the abort code 187 | #[expected_failure(abort_code = 0)] 188 | fun make_sure_zero_coin_fails() { 189 | let coin = MyCoin { value: 0 }; 190 | let MyCoin { value: _ } = make_sure_non_zero_coin(coin); 191 | } 192 | 193 | #[test_only] // 仅用作测试的帮助方法 // test only helper function 194 | fun publish_coin(account: &signer) { 195 | move_to(account, MyCoin { value: 1 }) 196 | } 197 | 198 | #[test(a = @0x1, b = @0x2)] 199 | fun test_has_coin(a: signer, b: signer) { 200 | publish_coin(&a); 201 | publish_coin(&b); 202 | assert!(has_coin(@0x1), 0); 203 | assert!(has_coin(@0x2), 1); 204 | assert!(!has_coin(@0x3), 1); 205 | } 206 | } 207 | ``` 208 | 209 | ### 运行测试(Running Tests) 210 | 211 | You can then run these tests with the `move test` command: 212 | 213 | 你可以使用 `move test` 命令运行测试。 214 | 215 | ``` 216 | $ move test 217 | BUILDING MoveStdlib 218 | BUILDING TestExample 219 | Running Move unit tests 220 | [ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes 221 | [ PASS ] 0x1::my_module::make_sure_zero_coin_fails 222 | [ PASS ] 0x1::my_module::test_has_coin 223 | Test result: OK. Total tests: 3; passed: 3; failed: 0 224 | ``` 225 | 226 | ### 使用测试参数(Using Test Flags) 227 | 228 | #### `-f ` 或 `--filter `(`-f ` or `--filter `) 229 | 230 | This will only run tests whose fully qualified name contains ``. For example if we wanted to only run tests with `"zero_coin"` in their name: 231 | 232 | 仅运行名字包含 `` 字符的测试。例如只想运行名字包含 `"zero_coin"` 的测试: 233 | 234 | 235 | ``` 236 | $ move test -f zero_coin 237 | CACHED MoveStdlib 238 | BUILDING TestExample 239 | Running Move unit tests 240 | [ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes 241 | [ PASS ] 0x1::my_module::make_sure_zero_coin_fails 242 | Test result: OK. Total tests: 2; passed: 2; failed: 0 243 | ``` 244 | 245 | #### `-i ` 或 `--instructions `(`-i ` or `--instructions `) 246 | 247 | This bounds the number of instructions that can be executed for any one test to ``: 248 | 249 | 调整测试指令限制为 ``: 250 | 251 | ``` 252 | $ move test -i 0 253 | CACHED MoveStdlib 254 | BUILDING TestExample 255 | Running Move unit tests 256 | [ TIMEOUT ] 0x1::my_module::make_sure_non_zero_coin_passes 257 | [ TIMEOUT ] 0x1::my_module::make_sure_zero_coin_fails 258 | [ TIMEOUT ] 0x1::my_module::test_has_coin 259 | 260 | Test failures: 261 | 262 | Failures in 0x1::my_module: 263 | 264 | ┌── make_sure_non_zero_coin_passes ────── 265 | │ Test timed out 266 | └────────────────── 267 | 268 | 269 | ┌── make_sure_zero_coin_fails ────── 270 | │ Test timed out 271 | └────────────────── 272 | 273 | 274 | ┌── test_has_coin ────── 275 | │ Test timed out 276 | └────────────────── 277 | 278 | Test result: FAILED. Total tests: 3; passed: 0; failed: 3 279 | ``` 280 | 281 | #### `-s` 或 `--statistics`(`-s` or `--statistics`) 282 | 283 | With these flags you can gather statistics about the tests run and report the runtime and instructions executed for each test. For example, if we wanted to see the statistics for the tests in the example above: 284 | 285 | 使用此参数你可以得到每个测试的运行报告及执行指令的统计信息。例如查看上述示例的统计数据: 286 | 287 | ``` 288 | $ move test -s 289 | CACHED MoveStdlib 290 | BUILDING TestExample 291 | Running Move unit tests 292 | [ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes 293 | [ PASS ] 0x1::my_module::make_sure_zero_coin_fails 294 | [ PASS ] 0x1::my_module::test_has_coin 295 | 296 | Test Statistics: 297 | 298 | ┌────────────────────────────────────────────────┬────────────┬───────────────────────────┐ 299 | │ Test Name │ Time │ Instructions Executed │ 300 | ├────────────────────────────────────────────────┼────────────┼───────────────────────────┤ 301 | │ 0x1::my_module::make_sure_non_zero_coin_passes │ 0.009 │ 1 │ 302 | ├────────────────────────────────────────────────┼────────────┼───────────────────────────┤ 303 | │ 0x1::my_module::make_sure_zero_coin_fails │ 0.008 │ 1 │ 304 | ├────────────────────────────────────────────────┼────────────┼───────────────────────────┤ 305 | │ 0x1::my_module::test_has_coin │ 0.008 │ 1 │ 306 | └────────────────────────────────────────────────┴────────────┴───────────────────────────┘ 307 | 308 | Test result: OK. Total tests: 3; passed: 3; failed: 0 309 | ``` 310 | 311 | #### `-g` 或 `--state-on-error`(`-g` or `--state-on-error`) 312 | 313 | These flags will print the global state for any test failures. e.g., if we added the following (failing) test to the `my_module` example: 314 | 315 | 这个参数会在测试失败情况下打印全局状态。如在 `my_module` 模块中添加下述失败测试: 316 | 317 | ``` 318 | module 0x1::my_module { 319 | ... 320 | #[test(a = @0x1)] 321 | fun test_has_coin_bad(a: signer) { 322 | publish_coin(&a); 323 | assert!(has_coin(@0x1), 0); 324 | assert!(has_coin(@0x2), 1); 325 | } 326 | } 327 | ``` 328 | 329 | we would get get the following output when running the tests: 330 | 331 | 当运行测试时我们将得到下面的输出: 332 | 333 | ``` 334 | $ move test -g 335 | CACHED MoveStdlib 336 | BUILDING TestExample 337 | Running Move unit tests 338 | [ PASS ] 0x1::my_module::make_sure_non_zero_coin_passes 339 | [ PASS ] 0x1::my_module::make_sure_zero_coin_fails 340 | [ PASS ] 0x1::my_module::test_has_coin 341 | [ FAIL ] 0x1::my_module::test_has_coin_bad 342 | 343 | Test failures: 344 | 345 | Failures in 0x1::my_module: 346 | 347 | ┌── test_has_coin_bad ────── 348 | │ error[E11001]: test failure 349 | │ ┌─ /home/tzakian/TestExample/sources/my_module.move:47:10 350 | │ │ 351 | │ 44 │ fun test_has_coin_bad(a: signer) { 352 | │ │ ----------------- In this function in 0x1::my_module 353 | │ · 354 | │ 47 │ assert!(has_coin(@0x2), 1); 355 | │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Test was not expected to abort but it aborted with 1 here 356 | │ 357 | │ 358 | │ ────── Storage state at point of failure ────── 359 | │ 0x1: 360 | │ => key 0x1::my_module::MyCoin { 361 | │ value: 1 362 | │ } 363 | │ 364 | └────────────────── 365 | 366 | Test result: FAILED. Total tests: 4; passed: 3; failed: 1 367 | ``` 368 | -------------------------------------------------------------------------------- /src/uses.md: -------------------------------------------------------------------------------- 1 | # 使用与别名(Uses and Aliases) 2 | 3 | The `use` syntax can be used to create aliases to members in other modules. `use` can be used to 4 | create aliases that last either for the entire module, or for a given expression block scope. 5 | 6 | `use` 语法可用于为其他模块中的成员创建别名。 `use` 可用于创建持续整个模块或给定表达式块范围的别名。 7 | 8 | ## 语法(Syntax) 9 | There are several different syntax cases for `use`. Starting with the most simple, we have the 10 | following for creating aliases to other modules 11 | 12 | 这里有几种不同的语法案例可供使用。从最简单的开始,我们有以下例子用于为其他模块创建别名 13 | 14 | ```move 15 | use
::; 16 | use
:: as ; 17 | ``` 18 | For example 19 | 举例 20 | ```move 21 | use std::vector; 22 | use std::vector as V; 23 | ``` 24 | 25 | `use std::vector;` introduces an alias `vector` for `std::vector`. This means that anywhere you 26 | would want to use the module name `std::vector` (assuming this `use` is in scope), you could use 27 | `vector` instead. `use std::vector;` is equivalent to `use std::vector as vector;` 28 | 29 | `use std::vector;`为 `std::vector` 引入别名向量。这意味着在任何您想使用模块名称 `std::vector` 的地方(假设此`use`在作用域内),您都可以使用 `vector` 代替使用`std::vector`; 30 | 31 | Similarly `use std::vector as V;` would let you use `V` instead of `std::vector` 32 | 33 | 同样使用 `std::vector as V`;会让你使用 `V` 代替 `std::vector` 34 | 35 | ```move 36 | use std::vector; 37 | use std::vector as V; 38 | 39 | fun new_vecs(): (vector, vector, vector) { 40 | let v1 = std::vector::empty(); 41 | let v2 = vector::empty(); 42 | let v3 = V::empty(); 43 | (v1, v2, v3) 44 | } 45 | ``` 46 | If you want to import a specific module member (such as a function, struct, or constant). You can 47 | use the following syntax. 48 | 49 | 如果要导入特定的模块成员(例如函数、结构或常量)。您可以使用以下语法。 50 | ```move 51 | use
::::; 52 | use
:::: as ; 53 | ``` 54 | For example 55 | 举例 56 | 57 | ```move 58 | use std::vector::empty; 59 | use std::vector::empty as empty_vec; 60 | ``` 61 | 62 | This would let you use the function `std::vector::empty` without full qualification. Instead you 63 | could use `empty` and `empty_vec` respectively. Again, `use std::vector::empty;` is equivalent to 64 | `use std::vector::empty as empty;` 65 | 66 | 这将允许您在没有前缀限定的情况下使用函数 `std::vector::empty`。相反,您可以分别使用 `empty` 和 `empty_vec`,使用 `std::vector::empty;`使用`empty` 相当于使用`std::vector::empty`; 67 | 68 | ```move 69 | use std::vector::empty; 70 | use std::vector::empty as empty_vec; 71 | 72 | fun new_vecs(): (vector, vector, vector) { 73 | let v1 = std::vector::empty(); 74 | let v2 = empty(); 75 | let v3 = empty_vec(); 76 | (v1, v2, v3) 77 | } 78 | ``` 79 | If you want to add aliases for multiple module members at once, you can do so with the following 80 | syntax 81 | 82 | 如果要一次为多个模块成员添加别名,可以使用以下语法 83 | ```move 84 | use
::::{, as ... }; 85 | ``` 86 | For example 87 | 举例 88 | 89 | ```move 90 | use std::vector::{push_back, length as len, pop_back}; 91 | 92 | fun swap_last_two(v: &mut vector) { 93 | assert!(len(v) >= 2, 42); 94 | let last = pop_back(v); 95 | let second_to_last = pop_back(v); 96 | push_back(v, last); 97 | push_back(v, second_to_last) 98 | } 99 | ``` 100 | 101 | If you need to add an alias to the Module itself in addition to module members, you can do that in a 102 | single `use` using `Self`. `Self` is a member of sorts that refers to the module. 103 | 104 | 如果除了模块成员之外,您还需要为模块本身添加别名,您可以使用 `Self` 在一次`use`中完成。 `Self` 是指模块的各种成员。 105 | ```move 106 | use std::vector::{Self, empty}; 107 | For clarity, all of the following are equivalent: 108 | ``` 109 | For clarity, all of the following are equivalent: 110 | 111 | 为清晰起见,以下所有内容都是等效的: 112 | ```move 113 | use std::vector; 114 | use std::vector as vector; 115 | use std::vector::Self; 116 | use std::vector::Self as vector; 117 | use std::vector::{Self}; 118 | use std::vector::{Self as vector}; 119 | ``` 120 | If needed, you can have as many aliases for any item as you like 121 | 122 | 如果需要,您可以为任何项目设置任意数量的别名 123 | 124 | ```move 125 | use std::vector::{ 126 | Self, 127 | Self as V, 128 | length, 129 | length as len, 130 | }; 131 | 132 | fun pop_twice(v: &mut vector): (T, T) { 133 | // all options available given the `use` above 134 | assert!(vector::length(v) > 1, 42); 135 | assert!(V::length(v) > 1, 42); 136 | assert!(length(v) > 1, 42); 137 | assert!(len(v) > 1, 42); 138 | 139 | (vector::pop_back(v), vector::pop_back(v)) 140 | } 141 | ``` 142 | 143 | ## 模块内部(Inside a `module`) 144 | Inside of a `module` all `use` declarations are usable regardless of the order of declaration. 145 | 146 | 在模块内部,无论声明顺序如何,所有 `use` 声明都是可用的。 147 | ```move 148 | address 0x42 { 149 | module example { 150 | use std::vector; 151 | 152 | fun example(): vector { 153 | let v = empty(); 154 | vector::push_back(&mut v, 0); 155 | vector::push_back(&mut v, 10); 156 | v 157 | } 158 | 159 | use std::vector::empty; 160 | } 161 | } 162 | ``` 163 | The aliases declared by `use` in the module usable within that module. 164 | 165 | 在该模块中可用的模块中使用声明的别名。 166 | 167 | Additionally, the aliases introduced cannot conflict with other module members. See 168 | [Uniqueness](#uniqueness) for more details 169 | 170 | 此外,引入的别名不能与其他模块成员冲突。有关详细信息,请参阅[唯一性](#uniqueness)。 171 | 172 | ## 表达式内部(Inside an expression) 173 | You can add `use` declarations to the beginning of any expression block 174 | 175 | 您可以将 `use` 声明添加到任何表达式块的开头 176 | ```move 177 | address 0x42 { 178 | module example { 179 | 180 | fun example(): vector { 181 | use std::vector::{empty, push_back}; 182 | 183 | let v = empty(); 184 | push_back(&mut v, 0); 185 | push_back(&mut v, 10); 186 | v 187 | } 188 | } 189 | } 190 | ``` 191 | As with `let`, the aliases introduced by `use` in an expression block are removed at the end of that 192 | block. 193 | 194 | 与 `let` 一样,在表达式块中使用 `use` 引入的别名在该块的末尾被删除。 195 | 196 | ```move 197 | address 0x42 { 198 | module example { 199 | 200 | fun example(): vector { 201 | let result = { 202 | use std::vector::{empty, push_back}; 203 | let v = empty(); 204 | push_back(&mut v, 0); 205 | push_back(&mut v, 10); 206 | v 207 | }; 208 | result 209 | } 210 | 211 | } 212 | } 213 | ``` 214 | Attempting to use the alias after the block ends will result in an error 215 | 216 | 在块结束后尝试使用别名将导致错误 217 | ```move 218 | fun example(): vector { 219 | let result = { 220 | use std::vector::{empty, push_back}; 221 | let v = empty(); 222 | push_back(&mut v, 0); 223 | push_back(&mut v, 10); 224 | v 225 | }; 226 | let v2 = empty(); // 错误! 227 | // ^^^^^ 未绑定的函数 'empty' 228 | 结果 229 | } 230 | ``` 231 | Any `use` must be the first item in the block. If the `use` comes after any expression or `let`, it 232 | will result in a parsing error 233 | 234 | 任何使用都必须是块中的第一项。如果 use 出现在任何表达式或 let 之后,则会导致解析错误 235 | ```move 236 | { 237 | let x = 0; 238 | use std::vector; // 错误! 239 | let v = vector::empty(); 240 | } 241 | ``` 242 | 243 | ## 命名规则(Naming rules) 244 | Aliases must follow the same rules as other module members. This means that aliases to structs or 245 | constants must start with `A` to `Z` 246 | 247 | 别名必须遵循与其他模块成员相同的规则。这意味着结构或常量的别名必须以 `A` 到 `Z` 开头 248 | ```move 249 | address 0x42 { 250 | module data { 251 | struct S {} 252 | const FLAG: bool = false; 253 | fun foo() {} 254 | } 255 | module example { 256 | use 0x42::data::{ 257 | S as s, // 错误! 258 | FLAG as fLAG, // 错误! 259 | foo as FOO, // 有效 260 | foo as bar, // 有效 261 | }; 262 | } 263 | } 264 | ``` 265 | ## 唯一性(Uniqueness) 266 | Inside a given scope, all aliases introduced by `use` declarations must be unique. 267 | 268 | 在给定范围内,所有由 use 声明引入的别名必须是唯一的。 269 | 270 | For a module, this means aliases introduced by `use` cannot overlap 271 | 272 | 对于一个模块,这意味着使用引入的别名不能重复 273 | ```move 274 | address 0x42 { 275 | module example { 276 | 277 | use std::vector::{empty as foo, length as foo}; // ERROR! 278 | // ^^^ duplicate 'foo' 279 | 280 | use std::vector::empty as bar; 281 | 282 | use std::vector::length as bar; // 错误! 283 | // ^^^ 重复的 'bar' 284 | 285 | } 286 | } 287 | ``` 288 | And, they cannot overlap with any of the module's other members 289 | 290 | 而且,它们不能与模块的任何其他成员重复 291 | ```move 292 | address 0x42 { 293 | module data { 294 | struct S {} 295 | } 296 | module example { 297 | use 0x42::data::S; 298 | 299 | struct S { value: u64 } // ERROR! 300 | // ^ conflicts with alias 'S' above 301 | } 302 | } 303 | ``` 304 | Inside of an expression block, they cannot overlap with each other, but they can 305 | [shadow](#shadowing) other aliases or names from an outer scope 306 | 307 | 在表达式块内部,它们不能相互重复,但它们可以遮蔽外部作用域中的其他别名或名称 308 | 309 | ## 遮蔽(Shadowing) 310 | `use` aliases inside of an expression block can shadow names (module members or aliases) from the 311 | outer scope. As with shadowing of locals, the shadowing ends at the end of the expression block; 312 | 313 | 在表达式块内使用别名可以覆盖外部作用域的名称(模块成员或别名)。当遮蔽局部变量时,遮蔽会在表达式块的末尾结束; 314 | ```move 315 | address 0x42 { 316 | module example { 317 | 318 | struct WrappedVector { vec: vector } 319 | 320 | fun empty(): WrappedVector { 321 | WrappedVector { vec: std::vector::empty() } 322 | } 323 | 324 | fun example1(): (WrappedVector, WrappedVector) { 325 | let vec = { 326 | use std::vector::{empty, push_back}; 327 | // 'empty' 现在指向 std::vector::empty 328 | 329 | let v = empty(); 330 | push_back(&mut v, 0); 331 | push_back(&mut v, 1); 332 | push_back(&mut v, 10); 333 | v 334 | }; 335 | // 'empty' 现在指向 Self::empty 336 | 337 | (empty(), WrappedVector { vec }) 338 | } 339 | 340 | fun example2(): (WrappedVector, WrappedVector) { 341 | use std::vector::{empty, push_back}; 342 | let w: WrappedVector = { 343 | use 0x42::example::empty; 344 | empty() 345 | }; 346 | push_back(&mut w.vec, 0); 347 | push_back(&mut w.vec, 1); 348 | push_back(&mut w.vec, 10); 349 | 350 | let vec = empty(); 351 | push_back(&mut vec, 0); 352 | push_back(&mut vec, 1); 353 | push_back(&mut vec, 10); 354 | 355 | (w, WrappedVector { vec }) 356 | } 357 | } 358 | } 359 | ``` 360 | 361 | ## 未使用的Use或别名(Unused Use or Alias) 362 | An unused `use` will result in an error 363 | 364 | 未使用的 `use` 会导致错误 365 | ```move 366 | address 0x42 { 367 | module example { 368 | use std::vector::{empty, push_back}; // ERROR! 369 | // ^^^^^^^^^ 未使用的别名 'push_back' 370 | 371 | fun example(): vector { 372 | empty() 373 | } 374 | } 375 | } 376 | ``` 377 | -------------------------------------------------------------------------------- /src/vector.md: -------------------------------------------------------------------------------- 1 | # 向量(Vector) 2 | 3 | `vector` is the only primitive collection type provided by Move. A `vector` is a homogenous 4 | collection of `T`'s that can grow or shrink by pushing/popping values off the "end". 5 | 6 | A `vector` can be instantiated with any type `T`. For example, `vector`, `vector
`, 7 | `vector<0x42::MyModule::MyResource>`, and `vector>` are all valid vector types. 8 | 9 | `vector` 是 Move 提供的唯一原始集合类型。`vector` 是类型为 `T` 的同构集合,可以通过从"末端"推入/弹出(出栈/入栈,译者注)值来增长或缩小。 10 | *(与 Rust 一样,向量(vector)是一种可以存放任何类型的可变大小的容器,也可称为[动态数组](https://en.wikipedia.org/wiki/Dynamic_array),与 Python 中的[列表(list)](https://computersciencewiki.org/index.php/Lists)不同,译者注)* 11 | 12 | `vector` 可以用任何类型 `T` 实例化。例如,`vector`、`vector
`、`vector<0x42::MyModuel::MyResource>` 和 `vector>` 都是有效的向量类型。 13 | 14 | ## 字面量(Literals) 15 | 16 | ### 通用 `vector` 字面量(General `vector` Literals) 17 | 18 | Vectors of any type can be created with `vector` literals. 19 | 20 | 任何类型的向量都可以通过 `vector` 字面量创建。 21 | 22 | | 语法 | 类型 | 描述 | 23 | |-----------------------|-------------------------------------------------------------------------------|-----------------------------------| 24 | | `vector[]` | `vector[]: vector` 其中 `T` 是任何单一的非引用类型 | 一个空向量 | 25 | | `vector[e1, ..., en]` | `vector[e1, ..., en]: vector` where `e_i: T` 满足 `0 < i <= n` and `n > 0` | 带有 `n` 个元素(长度为 n)的向量 | 26 | 27 | In these cases, the type of the `vector` is inferred, either from the element type or from the 28 | vector's usage. If the type cannot be inferred, or simply for added clarity, the type can be 29 | specified explicitly: 30 | 31 | 在这些情况下,`vector` 的类型是从元素类型或从向量的使用上推断出来的。如果无法推断类型或者只是为了更清楚地表示,则可以显式指定类型: 32 | 33 | ```move 34 | vector[]: vector 35 | vector[e1, ..., en]: vector 36 | ``` 37 | 38 | #### 向量字面量示例(Example Vector Literals) 39 | 40 | ```move 41 | (vector[]: vector); 42 | (vector[0u8, 1u8, 2u8]: vector); 43 | (vector[]: vector); 44 | (vector
[@0x42, @0x100]: vector
); 45 | ``` 46 | 47 | ### `vector` 字面量(`vector` literals) 48 | 49 | A common use-case for vectors in Move is to represent "byte arrays", which are represented with 50 | `vector`. These values are often used for cryptographic purposes, such as a public key or a hash 51 | result. These values are so common that specific syntax is provided to make the values more 52 | readable, as opposed to having to use `vector[]` where each individual `u8` value is specified in 53 | numeric form. 54 | 55 | There are currently two supported types of `vector` literals, *byte strings* and *hex strings*. 56 | 57 | Move 中向量的一个常见用例是表示“字节数组”,用 `vector` 表示。这些值通常用于加密目的,例如公钥或哈希结果。这些值非常常见,以至于提供了特定的语法以使值更具可读性,而不是必须使用 `vector[]`,其中每个单独的 `u8` 值都以数字形式指定。 58 | 59 | 目前支持两种类型的 `vector` 字面量,*字节字符串*和*十六进制字符串*。 60 | 61 | #### 字节字符串(Byte Strings) 62 | 63 | Byte strings are quoted string literals prefixed by a `b`, e.g. `b"Hello!\n"`. 64 | 65 | These are ASCII encoded strings that allow for escape sequences. Currently, the supported escape 66 | sequences are 67 | 68 | 字节字符串是带引号的字符串字面量,以 `b` 为前缀,例如,`b"Hello!\n"`。 69 | 70 | 这些是允许转义序列的 ASCII 编码字符串。目前,支持的转义序列如下: 71 | 72 | | 转义序列 | 描述 | 73 | |----------|---------------------------------------------| 74 | | `\n` | 换行 | 75 | | `\r` | 回车 | 76 | | `\t` | 制表符 | 77 | | `\\` | 反斜杠 | 78 | | `\0` | Null | 79 | | `\"` | 引号 | 80 | | `\xHH` | 十六进制进制转义,插入十六进制字节序列 `HH` | 81 | 82 | #### 十六进制字符串(Hex Strings) 83 | 84 | Hex strings are quoted string literals prefixed by a `x`, e.g. `x"48656C6C6F210A"` 85 | 86 | Each byte pair, ranging from `00` to `FF`, is interpreted as hex encoded `u8` value. So each byte 87 | pair corresponds to a single entry in the resulting `vector` 88 | 89 | 十六进制字符串是以 `x` 为前缀的带引号的字符串字面量,例如,`x"48656C6C6F210A"`。 90 | 91 | 每个字节对,范围从 `00` 到 `FF` 都被解析为十六进制编码的 `u8` 值。所以每个字节对对应于结果 `vector` 的单个条目。 92 | 93 | #### 字符串字面量示例(Example String Literals) 94 | 95 | ```move 96 | script { 97 | fun byte_and_hex_strings() { 98 | assert!(b"" == x"", 0); 99 | assert!(b"Hello!\n" == x"48656C6C6F210A", 1); 100 | assert!(b"\x48\x65\x6C\x6C\x6F\x21\x0A" == x"48656C6C6F210A", 2); 101 | assert!( 102 | b"\"Hello\tworld!\"\n \r \\Null=\0" == 103 | x"2248656C6C6F09776F726C6421220A200D205C4E756C6C3D00", 104 | 3 105 | ); 106 | } 107 | } 108 | ``` 109 | 110 | ## 操作 (Operations) 111 | 112 | `vector` supports the following operations via the `std::vector` module in the Move standard 113 | library: 114 | 115 | `vector` 通过 Move 标准库里的 `std::vector` 模块支持以下操作: 116 | 117 | | 函数 | 描述 | 中止条件 | 118 | |------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|----------------------| 119 | | `vector::empty(): vector` | 创建一个可以存储 `T` 类型值的空向量 | 永不中止 | 120 | | `vector::singleton(t: T): vector` | 创建一个包含 `t` 的大小为 1 的向量 | 永不中止 | 121 | | `vector::push_back(v: &mut vector, t: T)` | 将 `t` 添加到 `v` 的尾部 | 永不中止 | 122 | | `vector::pop_back(v: &mut vector): T` | 移除并返回 `v` 中的最后一个元素 | 如果 `v` 是空向量 | 123 | | `vector::borrow(v: &vector, i: u64): &T` | 返回在索引 `i` 处对 `T` 的不可变引用 | 如果 `i` 越界 | 124 | | `vector::borrow_mut(v: &mut vector, i: u64): &mut T` | 返回在索引 `i` 处对 `T` 的可变引用 | 如果 `i` 越界 | 125 | | `vector::destroy_empty(v: vector)` | 销毁 `v` 向量 | 如果 `v` 不是空向量 | 126 | | `vector::append(v1: &mut vector, v2: vector)` | 将 `v2` 中的元素添加到 `v1` 的末尾 | 永不中止 | 127 | | `vector::contains(v: &vector, e: &T): bool` | 如果 `e` 在向量 `v` 里返回 true,否则返回 false | 永不中止 | 128 | | `vector::swap(v: &mut vector, i: u64, j: u64)` | 交换向量 `v` 中第 `i` 个和第 `j` 个索引处的元素 | 如果 `i` 或 `j` 越界 | 129 | | `vector::reverse(v: &mut vector)` | 反转向量 `v` 中元素的顺序 | 永不中止 | 130 | | `vector::index_of(v: &vector, e: &T): (bool, u64)` | 如果 `e` 在索引 `i` 处的向量中,则返回 `(true, i)`。否则返回`(false, 0)` | 永不中止 | 131 | | `vector::remove(v: &mut vector, i: u64): T` | 移除向量 `v` 中的第 `i` 个元素,移动所有后续元素。这里的时间复杂度是 O(n),并且保留了向量中元素的顺序 | 如果 `i` 越界 | 132 | | `vector::swap_remove(v: &mut vector, i: u64): T` | 将向量中的第 `i` 个元素与最后一个元素交换,然后弹出该元素。这里的时间复杂度是 O(1),但是不保留向量中的元素顺序 | 如果 `i` 越界 | 133 | 134 | More operations may be added over time. 135 | 136 | 随着时间的推移可能会增加更多操作。 137 | 138 | ## 示例 139 | 140 | ```move 141 | use std::vector; 142 | 143 | let v = vector::empty(); 144 | vector::push_back(&mut v, 5); 145 | vector::push_back(&mut v, 6); 146 | 147 | assert!(*vector::borrow(&v, 0) == 5, 42); 148 | assert!(*vector::borrow(&v, 1) == 6, 42); 149 | assert!(vector::pop_back(&mut v) == 6, 42); 150 | assert!(vector::pop_back(&mut v) == 5, 42); 151 | ``` 152 | 153 | ## 销毁和复制 `vector`(Destroying and copying `vector`) 154 | 155 | Some behaviors of `vector` depend on the abilities of the element type, `T`. For example, vectors 156 | containing elements that do not have `drop` cannot be implicitly discarded like `v` in the example 157 | above--they must be explicitly destroyed with `vector::destroy_empty`. 158 | 159 | Note that `vector::destroy_empty` will abort at runtime unless `vec` contains zero elements: 160 | 161 | `vector` 的某些行为取决于元素类型 `T` 的能力(ability),例如:如果向量中包含不具有 `drop` 能力的元素,那么不能像上面例子中的 `v` 一样隐式丢弃 —— 它们必须用 `vector::destroy_empty` 显式销毁。 162 | 163 | 请注意,除非向量 `vec` 包含零个元素,否则 `vector::destroy_empty` 将在运行时中止: 164 | 165 | ```move 166 | fun destroy_any_vector(vec: vector) { 167 | vector::destroy_empty(vec) // 删除此行将导致编译器错误 168 | } 169 | ``` 170 | 171 | 但是删除包含带有 `drop` 能力的元素的向量不会发生错误: 172 | 173 | ```move 174 | fun destroy_droppable_vector(vec: vector) { 175 | // 有效! 176 | // 不需要明确地做任何事情来销毁向量 177 | } 178 | ``` 179 | 180 | Similarly, vectors cannot be copied unless the element type has `copy`. In other words, a 181 | `vector` has `copy` if and only if `T` has `copy`. However, even copyable vectors are never 182 | implicitly copied: 183 | 184 | 同样,除非元素类型具有 `copy` 能力,否则无法复制向量。换句话说,当且仅当 `T` 具有 `copy` 能力时,`vector` 才具有 `copy` 能力。然而,即使是可复制的向量也永远不会被隐式复制: 185 | 186 | 换句话说,`vector` 有 `copy` 能力当且仅当 `T` 有 `copy` 能力。然而,即使是可复制的向量也永远不会被隐式复制: 187 | 188 | ```move 189 | let x = vector::singleton(10); 190 | let y = copy x; // 没有 copy 将导致编译器错误! 191 | ``` 192 | 193 | Copies of large vectors can be expensive, so the compiler requires explicit `copy`'s to make it 194 | easier to see where they are happening. 195 | 196 | For more details see the sections on [type abilities](./abilities.md) and [generics](./generics.md). 197 | 198 | 大向量的复制可能很昂贵,因此编译器需要显式 `copy` 以便更容易查看它们发生的位置。 199 | 200 | 有关更多详细信息,请参阅[类型能力](./abilities.md)和[泛型](./generics.md)部分。 201 | 202 | ## 所有权(Ownership) 203 | 204 | [如上所述](#销毁和复制-vectordestroying-and-copying-vector),`vector` 值只有在元素值可以复制的时候才能复制。在这种情况下,复制必须通过显式 [`copy`](./variables.md#移动和复制move-and-copy) 或者[解引用 `*`](./references.md#引用运算符reference-operators)。 205 | --------------------------------------------------------------------------------