├── .github └── FUNDING.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md └── guide.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [knsh14] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2019-10-19 2 | 3 | - Initial release. 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at oss-conduct@uber.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /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 | https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md# uber-style-guide-ja 2 | 3 | https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md# About 4 | [uber-go/guide]( https://github.com/uber-go/guide )の日本語訳 5 | 6 | https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md# Table of Contents 7 | - [導入](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#導入) 8 | - [ガイドライン](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#ガイドライン) 9 | - [Pointers to Interfaces](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#pointers-to-interfaces) 10 | - [Verify Interface Compliance](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#verify-interface-compliance) 11 | - [Receivers and Interfaces](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#receivers-and-interfaces) 12 | - [Zero-value Mutexes are Valid](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#zero-value-mutexes-are-valid) 13 | - [Copy Slices and Maps at Boundaries](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#copy-slices-and-maps-at-boundaries) 14 | - [Receiving Slices and Maps](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#receiving-slices-and-maps) 15 | - [Returning Slices and Maps](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#returning-slices-and-maps) 16 | - [Defer to Clean Up](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#defer-to-clean-up) 17 | - [Channel Size is One or None](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#channel-size-is-one-or-none) 18 | - [Start Enums at One](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#start-enums-at-one) 19 | - [Use `"time"` to handle time](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-time-to-handle-time) 20 | - [Use `time.Time` for instants of time](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-timetime-for-instants-of-time) 21 | - [Use `time.Duration` for periods of time](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-timeduration-for-periods-of-time) 22 | - [Use `time.Time` and `time.Duration` with external systems](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-timetime-and-timeduration-with-external-systems) 23 | - [Errors](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#errors) 24 | - [Error Types](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#error-types) 25 | - [Error Wrapping](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#error-wrapping) 26 | - [Error Naming](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#error-naming) 27 | - [Handle Errors Once](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#handle-errors-once) 28 | - [Handle Type Assertion Failures](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#handle-type-assertion-failures) 29 | - [Don't Panic](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#dont-panic) 30 | - [Use go.uber.org/atomic](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-gouberorgatomic) 31 | - [Avoid Mutable Globals](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-mutable-globals) 32 | - [Avoid Embedding Types in Public Structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-embedding-types-in-public-structs) 33 | - [Avoid Using Built-in Names](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-using-built-in-names) 34 | - [Avoid `init()`](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-init) 35 | - [Exit in Main](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#exit-in-main) 36 | - [Exit Once](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#exit-once) 37 | - [Use field tags in marshaled structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-field-tags-in-marshaled-structs) 38 | - [Don't fire-and-forget goroutines](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#dont-fire-and-forget-goroutines) 39 | - [Wait for goroutines to exit](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#wait-for-goroutines-to-exit) 40 | - [No goroutines in `init()`](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#no-goroutines-in-init) 41 | - [Performance](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#performance) 42 | - [Prefer strconv over fmt](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#prefer-strconv-over-fmt) 43 | - [Avoid string-to-byte conversion](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-string-to-byte-conversion) 44 | - [Prefer Specifying Map Capacity Hints](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#prefer-specifying-map-capacity-hints) 45 | - [Style](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#style) 46 | - [Avoid overly long lines](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-overly-long-lines) 47 | - [Be Consistent](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#be-consistent) 48 | - [Group Similar Declarations](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#group-similar-declarations) 49 | - [Import Group Ordering](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#import-group-ordering) 50 | - [Package Names](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#package-names) 51 | - [Function Names](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#function-names) 52 | - [Import Aliasing](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#import-aliasing) 53 | - [Function Grouping and Ordering](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#function-grouping-and-ordering) 54 | - [Reduce Nesting](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#reduce-nesting) 55 | - [Unnecessary Else](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#unnecessary-else) 56 | - [Top-level Variable Declarations](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#top-level-variable-declarations) 57 | - [Prefix Unexported Globals with \_](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#prefix-unexported-globals-with-_) 58 | - [Embedding in Structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#embedding-in-structs) 59 | - [Local Variable Declarations](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#local-variable-declarations) 60 | - [nil is a valid slice](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#nil-is-a-valid-slice) 61 | - [Reduce Scope of Variables](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#reduce-scope-of-variables) 62 | - [Avoid Naked Parameters](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-naked-parameters) 63 | - [Use Raw String Literals to Avoid Escaping](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-raw-string-literals-to-avoid-escaping) 64 | - [Initializing Structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#initializing-structs) 65 | - [Use Field Names to Initialize Structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-field-names-to-initialize-structs) 66 | - [Omit Zero Value Fields in Structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#omit-zero-value-fields-in-structs) 67 | - [Use `var` for Zero Value Structs](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#use-var-for-zero-value-structs) 68 | - [Initializing Struct References](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#initializing-struct-references) 69 | - [Initializing Maps](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#initializing-maps) 70 | - [Format Strings outside Printf](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#format-strings-outside-printf) 71 | - [Naming Printf-style Functions](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#naming-printf-style-functions) 72 | - [Patterns](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#patterns) 73 | - [Test Tables](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#test-tables) 74 | - [Avoid Unnecessary Complexity in Table Tests](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#avoid-unnecessary-complexity-in-table-tests) 75 | - [Parallel Tests](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#parallel-tests) 76 | - [Functional Options](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#functional-options) 77 | - [Linting](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#linting) 78 | - [Lint Runners](https://github.com/knsh14/uber-style-guide-ja/blob/master/guide.md#lint-runners) 79 | -------------------------------------------------------------------------------- /guide.md: -------------------------------------------------------------------------------- 1 | # 導入 2 | コーディングスタイルは私達のコードを統治する規則です。 3 | これらのスタイルは、gofmt がやってくれることから少しだけ発展したものです。 4 | 5 | このガイドのゴールはUber社内でのGoのコードでやるべき、もしくはやるべからずを説明し、コードの複雑さを管理することです。 6 | これらのルールはコードを管理しやすくし、かつエンジニアがGoの言語機能をより生産的に利用できるようにします。 7 | 8 | このガイドは元々同僚がGoを使ってより開発しやすくするために[Prashant Varanasi]( https://github.com/prashantv )と[Simon Newton]( https://github.com/nomis52 )によって作成されました。 9 | 長年にわたって多くのフィードバックを受けて修正されています。 10 | 11 | このドキュメントはUber社内で使われる規則を文書化したものです。 12 | 多くは以下のリソースでも見ることができるような一般的なものです。 13 | 14 | 1. Effective Go 15 | 2. The Go common mistakes guide 16 | 17 | 全てのコードは `golint` や `go vet` を通してエラーが出ない状態にするべきです。 18 | エディタに以下の設定を導入することを推奨しています。 19 | 20 | 1. 保存するごとに `goimports` を実行する 21 | 2. `golint` と `go vet` を実行してエラーがないかチェックする 22 | 23 | Goのエディタのサポートについては以下の資料を参考にしてください。 24 | https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins 25 | 26 | # ガイドライン 27 | ## Pointers to Interfaces 28 | インタフェースをポインタとして渡す必要はほぼありません。 29 | インタフェースは値として渡すべきです。 30 | ただインタフェースを実装している要素はポインタでも大丈夫です。 31 | 32 | インタフェースには2種類あります。 33 | 34 | 1. 型付けされた情報へのポインタ。これは type と考えることができます。 35 | 2. データポインタ。格納されたデータがポインタならそのまま使えます。格納されたデータが値ならその値のポインタになります。 36 | 37 | もしインタフェースのメソッドがそのインタフェースを満たした型のデータをいじりたいなら、インタフェースの裏側の型はポインタである必要があります。 38 | 39 | ## Verify Interface Compliance 40 | コンパイル時にインタフェースが適切に実装されているかチェックしましょう。 41 | これは以下のことを指します。 42 | 43 | * 公開された型がAPIとして適切に要求されたインタフェースを実装しているか 44 | * 公開されてるかどうかに関わらず、ある型の集合が同じインタフェースを実装しているか 45 | * その他にインタフェースを実装しなければ利用できなくなるケース 46 | 47 | 48 | 49 | 50 | 85 |
BadGood
51 | 52 | ```go 53 | type Handler struct { 54 | // ... 55 | } 56 | 57 | 58 | 59 | func (h *Handler) ServeHTTP( 60 | w http.ResponseWriter, 61 | r *http.Request, 62 | ) { 63 | ... 64 | } 65 | ``` 66 | 67 | 68 | 69 | ```go 70 | type Handler struct { 71 | // ... 72 | } 73 | 74 | var _ http.Handler = (*Handler)(nil) 75 | 76 | func (h *Handler) ServeHTTP( 77 | w http.ResponseWriter, 78 | r *http.Request, 79 | ) { 80 | // ... 81 | } 82 | ``` 83 | 84 |
86 | 87 | `var _ http.Handler = (*Handler)(nil)` という式は `*Handler` 型が `http.Handler` インタフェースを実装していなければ、コンパイルエラーになります。 88 | 89 | 代入式の右辺はゼロ値にするべきです。 90 | ポインタ型やスライス、マップなどは `nil` ですし、構造体ならその型の空の構造体にします。 91 | 92 | ```go 93 | type LogHandler struct { 94 | h http.Handler 95 | log *zap.Logger 96 | } 97 | 98 | var _ http.Handler = LogHandler{} 99 | 100 | func (h LogHandler) ServeHTTP( 101 | w http.ResponseWriter, 102 | r *http.Request, 103 | ) { 104 | // ... 105 | } 106 | ``` 107 | 108 | ## Receivers and Interfaces 109 | レシーバーが値のメソッドはレシーバーがポインタでも呼び出すことができますが、逆はできません。 110 | 111 | ```Go 112 | type S struct { 113 | data string 114 | } 115 | 116 | func (s S) Read() string { 117 | return s.data 118 | } 119 | 120 | func (s *S) Write(str string) { 121 | s.data = str 122 | } 123 | 124 | sVals := map[int]S{1: {"A"}} 125 | 126 | // You can only call Read using a value 127 | sVals[1].Read() 128 | 129 | // This will not compile: 130 | // sVals[1].Write("test") 131 | 132 | sPtrs := map[int]*S{1: {"A"}} 133 | 134 | // You can call both Read and Write using a pointer 135 | sPtrs[1].Read() 136 | sPtrs[1].Write("test") 137 | ``` 138 | 139 | 同じように、メソッドのレシーバーが値型でも、ポインタがインタフェースを満たしているとみなされます。 140 | 141 | ``` 142 | type F interface { 143 | f() 144 | } 145 | 146 | type S1 struct{} 147 | 148 | func (s S1) f() {} 149 | 150 | type S2 struct{} 151 | 152 | func (s *S2) f() {} 153 | 154 | s1Val := S1{} 155 | s1Ptr := &S1{} 156 | s2Val := S2{} 157 | s2Ptr := &S2{} 158 | 159 | var i F 160 | i = s1Val 161 | i = s1Ptr 162 | i = s2Ptr 163 | 164 | // The following doesn't compile, since s2Val is a value, and there is no value receiver for f. 165 | // i = s2Val 166 | ``` 167 | 168 | Effective Go の [Pointers vs Values]( https://golang.org/doc/effective_go.html#pointers_vs_values )を見るとよいでしょう。 169 | 170 | ## Zero-value Mutexes are Valid 171 | `sync.Mutex` や `sync.RWMutex` はゼロ値でも有効です。ポインタで扱う必要はありません。 172 | 173 | 174 | 175 | 176 | 191 |
BadGood
177 | 178 | ```go 179 | mu := new(sync.Mutex) 180 | mu.Lock() 181 | ``` 182 | 183 | 184 | 185 | ```go 186 | var mu sync.Mutex 187 | mu.Lock() 188 | ``` 189 | 190 |
192 | 193 | もし構造体のポインタを使う場合、mutexはポインタでないフィールドにする必要があります。 194 | 外部に公開されてない構造体なら、mutexを埋め込みで使うこともできます。 195 | 196 | 197 | 198 | 199 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 |
200 | 201 | ```go 202 | type smap struct { 203 | sync.Mutex // only for unexported types 204 | 205 | data map[string]string 206 | } 207 | 208 | func newSMap() *smap { 209 | return &smap{ 210 | data: make(map[string]string), 211 | } 212 | } 213 | 214 | func (m *smap) Get(k string) string { 215 | m.Lock() 216 | defer m.Unlock() 217 | 218 | return m.data[k] 219 | } 220 | ``` 221 | 222 | 223 | 224 | ```go 225 | type SMap struct { 226 | mu sync.Mutex 227 | 228 | data map[string]string 229 | } 230 | 231 | func NewSMap() *SMap { 232 | return &SMap{ 233 | data: make(map[string]string), 234 | } 235 | } 236 | 237 | func (m *SMap) Get(k string) string { 238 | m.mu.Lock() 239 | defer m.mu.Unlock() 240 | 241 | return m.data[k] 242 | } 243 | ``` 244 | 245 |
インターナルな型やmutexのインタフェースを実装している必要がある場合には埋め込みを使う公開されている型にはプライベートなフィールドを使う
254 | 255 | ## Copy Slices and Maps at Boundaries 256 | スライスやマップは内部でデータへのポインタが含まれています。なのでコピーする際には注意してください。 257 | 258 | ### Receiving Slices and Maps 259 | 引数として受け取ってフィールドに保存したスライスは、他の箇所でデータが書き換わる可能性があることを覚えておいてください。 260 | 261 | 262 | 263 | 264 | 265 | 280 | 296 | 297 | 298 | 299 |
Bad Good
266 | 267 | ```go 268 | func (d *Driver) SetTrips(trips []Trip) { 269 | d.trips = trips 270 | } 271 | 272 | trips := ... 273 | d1.SetTrips(trips) 274 | 275 | // ここで値が変わると d1.trips[0] も変わる 276 | trips[0] = ... 277 | ``` 278 | 279 | 281 | 282 | ```go 283 | func (d *Driver) SetTrips(trips []Trip) { 284 | d.trips = make([]Trip, len(trips)) 285 | copy(d.trips, trips) 286 | } 287 | 288 | trips := ... 289 | d1.SetTrips(trips) 290 | 291 | // d1.trips に変更が及ばない 292 | trips[0] = ... 293 | ``` 294 | 295 |
300 | 301 | ### Returning Slices and Maps 302 | 同じように、公開せずに内部に保持しているスライスやマップが変更されることもあります。 303 | 304 | 305 | 306 | 307 | 352 |
BadGood
308 | 309 | ```go 310 | type Stats struct { 311 | mu sync.Mutex 312 | counters map[string]int 313 | } 314 | 315 | // Snapshot returns the current stats. 316 | func (s *Stats) Snapshot() map[string]int { 317 | s.mu.Lock() 318 | defer s.mu.Unlock() 319 | 320 | return s.counters 321 | } 322 | 323 | // snapshot は mutex で守られない 324 | // レースコンディションが起きる 325 | snapshot := stats.Snapshot() 326 | ``` 327 | 328 | 329 | 330 | ```go 331 | type Stats struct { 332 | mu sync.Mutex 333 | counters map[string]int 334 | } 335 | 336 | func (s *Stats) Snapshot() map[string]int { 337 | s.mu.Lock() 338 | defer s.mu.Unlock() 339 | 340 | result := make(map[string]int, len(s.counters)) 341 | for k, v := range s.counters { 342 | result[k] = v 343 | } 344 | return result 345 | } 346 | 347 | // snapshot はただのコピーなので変更しても影響はない 348 | snapshot := stats.Snapshot() 349 | ``` 350 | 351 |
353 | 354 | ## Defer to Clean Up 355 | ファイルや mutex のロックなどをクリーンアップするために defer を使おう 356 | 357 | 358 | 359 | 360 | 395 |
BadGood
361 | 362 | ```go 363 | p.Lock() 364 | if p.count < 10 { 365 | p.Unlock() 366 | return p.count 367 | } 368 | 369 | p.count++ 370 | newCount := p.count 371 | p.Unlock() 372 | 373 | return newCount 374 | 375 | // easy to miss unlocks due to multiple returns 376 | ``` 377 | 378 | 379 | 380 | ```go 381 | p.Lock() 382 | defer p.Unlock() 383 | 384 | if p.count < 10 { 385 | return p.count 386 | } 387 | 388 | p.count++ 389 | return p.count 390 | 391 | // more readable 392 | ``` 393 | 394 |
396 | 397 | defer のオーバーヘッドは非常に小さいです。 398 | 関数の実行時間がナノ秒のオーダーである場合には避ける必要があります。 399 | defer を使ってほんの少しの実行コストを払えば可読性がとてもあがります。 400 | これはシンプルなメモリアクセス以上の計算が必要な大きなメソッドに特に当てはまります。 401 | 402 | ## Channel Size is One or None 403 | channel のサイズは普段は1もしくはバッファなしのものにするべきです。 404 | デフォルトでは channel はバッファなしでサイズが0になっています。 405 | それより大きいサイズにする場合はよく考える必要があります。 406 | どのようにしてサイズを決定するのか、チャネルがいっぱいになり処理がブロックされたときにどのような挙動をするかよく考える必要があります。 407 | 408 | 409 | 410 | 411 | 428 |
BadGood
412 | 413 | ```go 414 | // Ought to be enough for anybody! 415 | c := make(chan int, 64) 416 | ``` 417 | 418 | 419 | 420 | ```go 421 | // Size of one 422 | c := make(chan int, 1) // or 423 | // Unbuffered channel, size of zero 424 | c := make(chan int) 425 | ``` 426 | 427 |
429 | 430 | ## Start Enums at One 431 | Go で enum を導入するときの標準的な方法は、型を定義して `const` のグループを作り、初期値を `iota` にすることです。 432 | 変数のデフォルト値はゼロ値です。なので通常はゼロ値ではない値から enum を始めるべきでしょう。 433 | 434 | 435 | 436 | 437 | 466 |
BadGood
438 | 439 | ```go 440 | type Operation int 441 | 442 | const ( 443 | Add Operation = iota 444 | Subtract 445 | Multiply 446 | ) 447 | 448 | // Add=0, Subtract=1, Multiply=2 449 | ``` 450 | 451 | 452 | 453 | ```go 454 | type Operation int 455 | 456 | const ( 457 | Add Operation = iota + 1 458 | Subtract 459 | Multiply 460 | ) 461 | 462 | // Add=1, Subtract=2, Multiply=3 463 | ``` 464 | 465 |
467 | 468 | ただゼロ値を使うことに意味があるケースもあります。 469 | 例えばゼロ値をデフォルトの挙動として扱いたい場合です。 470 | 471 | ```go 472 | type LogOutput int 473 | 474 | const ( 475 | LogToStdout LogOutput = iota 476 | LogToFile 477 | LogToRemote 478 | ) 479 | 480 | // LogToStdout=0, LogToFile=1, LogToRemote=2 481 | ``` 482 | 483 | ## Use `"time"` to handle time 484 | 時間を正しく扱うのは非常に困難です。 485 | 時間に対する誤解には次のようなものがあります。 486 | 487 | 1. 1日は24時間である 488 | 2. 1時間は60分である 489 | 3. 1週間は7日である 490 | 4. 1年は365日である 491 | 5. [などなど]( https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time ) 492 | 493 | 例えば、1番について考えると、単純に24時間を足すだけでは正しくカレンダー上次の日になるとは限りません。 494 | 495 | そのため、時間を扱う場合は常に[time]( https://pkg.go.dev/time?tab=doc )パッケージを使いましょう。 496 | なぜならこのパッケージで前述の誤解を安全に処理することができるからです。 497 | 498 | ### Use `time.Time` for instants of time 499 | 時刻を扱うときは[time.Time]( https://pkg.go.dev/time?tab=doc#Time )型を使いましょう。 500 | また、時刻を比較したり、足し引きする際にも[time.Time]( https://pkg.go.dev/time?tab=doc#Time )型のメソッドを使いましょう 501 | 502 | 503 | 504 | 505 | 522 |
BadGood
506 | 507 | ```go 508 | func isActive(now, start, stop int) bool { 509 | return start <= now && now < stop 510 | } 511 | ``` 512 | 513 | 514 | 515 | ```go 516 | func isActive(now, start, stop time.Time) bool { 517 | return (start.Before(now) || start.Equal(now)) && now.Before(stop) 518 | } 519 | ``` 520 | 521 |
523 | 524 | ### Use `time.Duration` for periods of time 525 | 期間を扱うときには[time.Duration]( https://pkg.go.dev/time?tab=doc#Duration )型を使いましょう。 526 | 527 | 528 | 529 | 530 | 557 |
BadGood
531 | 532 | ```go 533 | func poll(delay int) { 534 | for { 535 | // ... 536 | time.Sleep(time.Duration(delay) * time.Millisecond) 537 | } 538 | } 539 | 540 | poll(10) // was it seconds or milliseconds? 541 | ``` 542 | 543 | 544 | 545 | ```go 546 | func poll(delay time.Duration) { 547 | for { 548 | // ... 549 | time.Sleep(delay) 550 | } 551 | } 552 | 553 | poll(10*time.Second) 554 | ``` 555 | 556 |
558 | 559 | 時刻に24時間を足す例に戻ります。 560 | もしカレンダー上で次の日の同じ時刻にしたい場合は [`time.AddDate`]( https://pkg.go.dev/time?tab=doc#Time.AddDate )メソッドを使います。 561 | もしその時刻から正確に24時間後にしたい場合は[`time.Add`]( https://pkg.go.dev/time?tab=doc#Time.Add )メソッドを使います。 562 | 563 | ```go 564 | newDay := t.AddDate(0 /* years */, 0, /* months */, 1 /* days */) 565 | maybeNewDay := t.Add(24 * time.Hour) 566 | ``` 567 | 568 | ### Use `time.Time` and `time.Duration` with external systems 569 | 570 | できるなら外部システムとのやり取りにも `time.Time` 型や `time.Duration` 型を使うようにしましょう。 571 | 572 | * Command-line flags: [`flag`]( https://golang.org/pkg/flag/ ) パッケージは[`time.ParseDuration`]( https://golang.org/pkg/time/#ParseDuration )を使うことで `time.Duration` 型をサポートできます 573 | * JSON: [`encoding/json`]( https://golang.org/pkg/encoding/json/ )パッケージは[`Unmarshal` メソッド]( https://golang.org/pkg/time/#Time.UnmarshalJSON )によって[RFC 3339]( https://tools.ietf.org/html/rfc3339 )フォーマットの時刻を `time.Time` 型にエンコーディングできます 574 | * SQL: [`database/sql`]( https://golang.org/pkg/database/sql/ )パッケージではもしドライバーがサポートしていれば `DATETIME` や `TIMESTAMP` 型のカラムを `time.Time` 型にすることができます 575 | * YAML: [`gopkg.in/yaml.v2`]( https://godoc.org/gopkg.in/yaml.v2 )パッケージは[`time.ParseDuration`]( https://golang.org/pkg/time/#ParseDuration )によって[RFC 3339]( https://tools.ietf.org/html/rfc3339 )フォーマットの時刻を `time.Time` 型にエンコーディングできます 576 | 577 | もし `time.Duration` が使えないなら、`int` 型や `float64` 型を使ってフィールド名に単位をもたせるようにしましょう。 578 | 次の表のようにします。 579 | 580 | 581 | 582 | 583 | 602 |
BadGood
584 | 585 | ```go 586 | // {"interval": 2} 587 | type Config struct { 588 | Interval int `json:"interval"` 589 | } 590 | ``` 591 | 592 | 593 | 594 | ```go 595 | // {"intervalMillis": 2000} 596 | type Config struct { 597 | IntervalMillis int `json:"intervalMillis"` 598 | } 599 | ``` 600 | 601 |
603 | 604 | もしこれらのインタラクションで `time.Time` を使えない場合には、[RFC 3339]( https://tools.ietf.org/html/rfc3339 )フォーマットの `string` 型を使うようにしましょう。 605 | このフォーマットは[time.UnmarshalText]( https://golang.org/pkg/time/#Time.UnmarshalText )メソッドの中でも使われますし、`time.Parse` や `time.Format` 関数でも [`time.RFC3339`]( https://pkg.go.dev/time?tab=doc#RFC3339 )と組み合わせて使えます。 606 | 607 | ## Errors 608 | 609 | ### Error Types 610 | エラーを定義する方法にはいくつかの種類があります。 611 | ユースケースに合った最適なものを選ぶために以下のことを考慮しましょう。 612 | 613 | * 呼び出し側は自身でエラーをハンドリングするためにエラーを検知する必要がありますか? 614 | その場合、上位のエラー変数または自前の型を定義することで [`errors.Is`] と [`errors.As`] 関数を利用できるようにサポートしなければなりません。 615 | * エラーメッセージは静的な文字列ですか?またはコンテキストを持つ情報が必要な動的な文字列ですか? 616 | 前者ならば [`errors.New`] が利用できます。後者ならば [`fmt.Errorf`] または自前のエラー型を利用しなければなりません。 617 | * 下流のエラーを更に上流に返していますか?もしそうならば[Error Wrappingのセクション](#error-wrapping)を参照してください。 618 | 619 | [`errors.Is`]: https://golang.org/pkg/errors/#Is 620 | [`errors.As`]: https://golang.org/pkg/errors/#As 621 | 622 | [`errors.New`]: https://golang.org/pkg/errors/#New 623 | [`fmt.Errorf`]: https://golang.org/pkg/fmt/#Errorf 624 | 625 | | エラーを検知? | エラーメッセージ | アドバイス | 626 | |-----------------|---------------|-----------------------------------| 627 | | いいえ | 静的 | [`errors.New`] | 628 | | いいえ | 動的 | [`fmt.Errorf`] | 629 | | はい | 静的 | [`errors.New`]を使ったパッケージ変数(`var`で定義) | 630 | | はい | 動的 | 自前のエラー型 | 631 | 632 | 例えば、静的文字列のエラーならば [`errors.New`] を利用しましょう。 633 | 呼び出し側がエラーを検知しハンドリングする必要がある場合は、そのエラーをパッケージ変数とし`errors.Is`で検知できるようにしましょう。 634 | 635 | 636 | 637 | 638 | 678 |
No error matchingError matching
639 | 640 | ```go 641 | // package foo 642 | 643 | func Open() error { 644 | return errors.New("could not open") 645 | } 646 | 647 | // package bar 648 | 649 | if err := foo.Open(); err != nil { 650 | // Can't handle the error. 651 | panic("unknown error") 652 | } 653 | ``` 654 | 655 | 656 | 657 | ```go 658 | // package foo 659 | 660 | var ErrCouldNotOpen = errors.New("could not open") 661 | 662 | func Open() error { 663 | return ErrCouldNotOpen 664 | } 665 | 666 | // package bar 667 | 668 | if err := foo.Open(); err != nil { 669 | if errors.Is(err, foo.ErrCouldNotOpen) { 670 | // handle the error 671 | } else { 672 | panic("unknown error") 673 | } 674 | } 675 | ``` 676 | 677 |
679 | 680 | 動的文字列のエラーの場合、呼び出し側がエラー検知する必要がないならば [`fmt.Errorf`] を使い、検知する必要があるならば自前の`error`インターフェースを実装する型を使いましょう。 681 | 682 | 683 | 684 | 685 | 733 |
No error matchingError matching
686 | 687 | ```go 688 | // package foo 689 | 690 | func Open(file string) error { 691 | return fmt.Errorf("file %q not found", file) 692 | } 693 | 694 | // package bar 695 | 696 | if err := foo.Open("testfile.txt"); err != nil { 697 | // Can't handle the error. 698 | panic("unknown error") 699 | } 700 | ``` 701 | 702 | 703 | 704 | ```go 705 | // package foo 706 | 707 | type NotFoundError struct { 708 | File string 709 | } 710 | 711 | func (e *NotFoundError) Error() string { 712 | return fmt.Sprintf("file %q not found", e.File) 713 | } 714 | 715 | func Open(file string) error { 716 | return &NotFoundError{File: file} 717 | } 718 | 719 | 720 | // package bar 721 | 722 | if err := foo.Open("testfile.txt"); err != nil { 723 | var notFound *NotFoundError 724 | if errors.As(err, ¬Found) { 725 | // handle the error 726 | } else { 727 | panic("unknown error") 728 | } 729 | } 730 | ``` 731 | 732 |
734 | 735 | 自前のエラー型を公開する場合、それもパッケージの公開APIの一部になることに留意しましょう。 736 | 737 | ```go 738 | // package foo 739 | 740 | type errNotFound struct { 741 | file string 742 | } 743 | 744 | func (e errNotFound) Error() string { 745 | return fmt.Sprintf("file %q not found", e.file) 746 | } 747 | 748 | func IsNotFoundError(err error) bool { 749 | _, ok := err.(errNotFound) 750 | return ok 751 | } 752 | 753 | func Open(file string) error { 754 | return errNotFound{file: file} 755 | } 756 | 757 | // package bar 758 | 759 | if err := foo.Open("foo"); err != nil { 760 | if foo.IsNotFoundError(err) { 761 | // handle 762 | } else { 763 | panic("unknown error") 764 | } 765 | } 766 | ``` 767 | 768 | ### Error Wrapping 769 | エラーを伝搬させるためには以下の3つの方法が主流です。 770 | 771 | * 受けたエラーをそのまま返す。 772 | * `fmt.Errorf` に `%w` を付けてコンテキストを追加する。 773 | * `fmt.Errorf` に `%v` を付けてコンテキストを追加する。 774 | 775 | 追加するコンテキストが無いならばエラーをそのまま返しましょう。これによりオリジナルのエラー型とメッセージが保たれます。これは下流のエラーメッセージにエラーがどこから来たか追うための十分な情報がある場合に適しています。 776 | 777 | 別の方法として、"connection refused"のような曖昧なエラーではなく、"call service foo: connection refused"のようなより有益なエラーを得られるように、可能な限りエラーメッセージにコンテキストを追加することもできます。 778 | 779 | エラーにコンテキストを追加するには`fmt.Errorf`を使いましょう。このとき、呼び出し側がエラー元の原因を抽出し検知できるようにするべきかどうかに基づき`%w`または`%v`を選ぶことになります。 780 | 781 | * 呼び出し側が原因のエラー元を把握する必要がある場合は`%w`を使いましょう。これはほとんどのラップされたエラーにとって良いデフォルトの振る舞いになりますが、呼び出し側がそれに依存しだすかもしないことを考慮しましょう。そのため、ラップされたエラーが既知の変数(var)か型(type)であるケースでは、関数の責務としてその振る舞いのコードドキュメント記載とテストをしましょう。 782 | * 原因のエラー元をあえて曖昧にする場合`%v`を使いましょう。呼び出し側はエラー検知をすることができなくなりますが、将来必要なときに`%w`を使うように変更できます。 783 | 784 | 返されたエラーにコンテキストを追加する場合、"failed to"のようなエラーがスタックに蓄積されるあたって明白な表現は避け、コンテキストを簡潔に保つようにしてください。 785 | 786 | 787 | 788 | 789 | 822 |
BadGood
790 | 791 | ```go 792 | s, err := store.New() 793 | if err != nil { 794 | return fmt.Errorf( 795 | "failed to create new store: %w", err) 796 | } 797 | ``` 798 | 799 | 800 | 801 | ```go 802 | s, err := store.New() 803 | if err != nil { 804 | return fmt.Errorf( 805 | "new store: %w", err) 806 | } 807 | ``` 808 | 809 |
810 | 811 | ``` 812 | failed to x: failed to y: failed to create new store: the error 813 | ``` 814 | 815 | 816 | 817 | ``` 818 | x: y: new store: the error 819 | ``` 820 | 821 |
823 | 824 | しかし、エラーメッセージが他のシステムに送られる場合は"err"タグを付けたり"Failed"プレフィックスをつけたりすることでエラーメッセージであることを明確にする必要があります。 825 | 826 | [Don't just check errors, handle them gracefully]の記事も参照してください。 827 | 828 | [Don't just check errors, handle them gracefully]: https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully 829 | 830 | ### Error Naming 831 | 832 | グローバル変数として使われるエラー値においてそれがパブリックかプライベートかによって`Err`または`err`のプレフィックスを付けましょう。 833 | この助言は[Prefix Unexported Globals with _](#prefix-unexported-globals-with-_)の助言よりも優先されます。 834 | 835 | ```go 836 | var ( 837 | // 以下の2つのエラーはパブリックであるため 838 | // このパッケージの利用者はこれらのエラーを 839 | // errors.Isで検知することができる。 840 | 841 | ErrBrokenLink = errors.New("link is broken") 842 | ErrCouldNotOpen = errors.New("could not open") 843 | 844 | // このエラーはパッケージのパブリックAPIに 845 | // させたくないのでプライベートにしている。 846 | // errors.Isでパッケージ内にてこのエラーを 847 | // 使うことができる。 848 | 849 | errNotFound = errors.New("not found") 850 | ) 851 | ``` 852 | 853 | カスタムエラー型の場合、`Error`を末尾に付けるようにしましょう。 854 | 855 | ```go 856 | // 同様にこのエラーはパブリックであるため 857 | // このパッケージの利用者はこれらのエラーを 858 | // errors.Asで検知することができる。 859 | 860 | type NotFoundError struct { 861 | File string 862 | } 863 | 864 | func (e *NotFoundError) Error() string { 865 | return fmt.Sprintf("file %q not found", e.File) 866 | } 867 | 868 | // このエラーはパッケージのパブリックAPIに 869 | // させたくないのでプライベートにしている。 870 | // errors.Asでパッケージ内にてこのエラーを 871 | // 使うことができる。 872 | 873 | type resolveError struct { 874 | Path string 875 | } 876 | 877 | func (e *resolveError) Error() string { 878 | return fmt.Sprintf("resolve %q", e.Path) 879 | } 880 | ``` 881 | 882 | ### Handle Errors Once 883 | 呼び出し側が呼び出し先からエラーを受け取ったとき、エラーについてどれほど知っているかで様々な方法があります。 884 | 885 | 主にこれらですが、他にも色々あります。 886 | 887 | - もし、呼び出し先が特定のエラーを定義しているなら、[`errors.Is`]や[`errors.As`]を使って処理を分岐させます。 888 | - もし復帰可能なエラーなら、エラーをログに出力し、安全に処理を継続しましょう 889 | - もしドメインで定義された条件を満たさないエラーなら、事前に定義したエラーを返しましょう 890 | - エラーを返すときは、[Wrap]( #error-wrapping ) するか、一つ一つ忠実に返しましょう。 891 | 892 | 呼び出し元がどのようにエラーを扱うかに関わらず、通常はどのエラーも一度だけ処理するべきです。例えば、呼び出し元の更に呼び出し元も同様にエラーを処理するので、呼び出し元はエラーをログに記録してから返すべきではありません。 893 | 894 | 次の具体例を考えます。 895 | 896 | 897 | 898 | 899 | 917 | 933 | 949 | 972 |
DescriptionCode
900 | 901 | **悪い例**: エラーをログに書き出してから返す。 902 | 903 | より上位の呼び出し先も同様にエラーをログに出力する可能性があるので、アプリケーションログに多くのノイズが混ざりこのログの価値が薄まります。 904 | 905 | 906 | 907 | ```go 908 | u, err := getUser(id) 909 | if err != nil { 910 | // BAD: See description 911 | log.Printf("Could not get user %q: %v", id, err) 912 | return err 913 | } 914 | ``` 915 | 916 |
918 | 919 | **良い例**: エラーをラップして返す。 920 | 921 | より上位の呼び出し側がエラーを処理します。 `%w` を使うと、[`errors.Is`]や[`errors.As`]を使ってエラーマッチさせることができます。 922 | 923 | 924 | 925 | ```go 926 | u, err := getUser(id) 927 | if err != nil { 928 | return fmt.Errorf("get user %q: %w", id, err) 929 | } 930 | ``` 931 | 932 |
934 | 935 | **良い例**: エラーをログに出力し、処理を継続する。 936 | 937 | もしその処理が絶対必要でないなら、品質は下がりますが復旧して処理を続けることができます。 938 | 939 | 940 | ```go 941 | if err := emitMetrics(); err != nil { 942 | // メトリクスの書き出し失敗は処理を止めるほどではない 943 | log.Printf("Could not emit metrics: %v", err) 944 | } 945 | 946 | ``` 947 | 948 |
950 | 951 | **良い例**: エラーマッチさせて処理を継続する。 952 | 953 | もし呼び出し元がエラーを定義していて、そのエラーが復旧可能なら、エラーを確認して処理を継続しましょう。 954 | その他のエラーだった場合はエラーをラップして返しましょう。 955 | 956 | ラップされたエラーは上位の呼び出し元で処理させましょう。 957 | 958 | 959 | ```go 960 | tz, err := getUserTimeZone(id) 961 | if err != nil { 962 | if errors.Is(err, ErrUserNotFound) { 963 | // もしユーザーがみつからないなら UTC を使う 964 | tz = time.UTC 965 | } else { 966 | return fmt.Errorf("get user %q: %w", id, err) 967 | } 968 | } 969 | ``` 970 | 971 |
973 | 974 | ## Handle Type Assertion Failures 975 | [型アサーション]( https://golang.org/ref/spec#Type_assertions )で1つの戻り値を受け取る場合、その型でなかったらパニックを起こします。 976 | 型アサーションではその型に変換できたかを示すbool値も同時に返ってくるので、それで事前にチェックしましょう。 977 | 978 | 979 | 980 | 981 | 997 |
BadGood
982 | 983 | ```go 984 | t := i.(string) 985 | ``` 986 | 987 | 988 | 989 | ```go 990 | t, ok := i.(string) 991 | if !ok { 992 | // 安全にエラーを処理する 993 | } 994 | ``` 995 | 996 |
998 | 999 | ## Don't Panic 1000 | プロダクションで動くコードはパニックを避けなければいけません。 1001 | パニックは連鎖的障害の主な原因です。 1002 | もしエラーが起きた場合、関数はエラーを返して、呼び出し元がどのようにエラーをハンドリングするか決めさせる必要があります。 1003 | 1004 | 1005 | 1006 | 1007 | 1049 |
BadGood
1008 | 1009 | ```go 1010 | func foo(bar string) { 1011 | if len(bar) == 0 { 1012 | panic("bar must not be empty") 1013 | } 1014 | // ... 1015 | } 1016 | 1017 | func main() { 1018 | if len(os.Args) != 2 { 1019 | fmt.Println("USAGE: foo ") 1020 | os.Exit(1) 1021 | } 1022 | foo(os.Args[1]) 1023 | } 1024 | ``` 1025 | 1026 | 1027 | 1028 | ```go 1029 | func foo(bar string) error { 1030 | if len(bar) == 0 { 1031 | return errors.New("bar must not be empty") 1032 | } 1033 | // ... 1034 | return nil 1035 | } 1036 | 1037 | func main() { 1038 | if len(os.Args) != 2 { 1039 | fmt.Println("USAGE: foo ") 1040 | os.Exit(1) 1041 | } 1042 | if err := foo(os.Args[1]); err != nil { 1043 | panic(err) 1044 | } 1045 | } 1046 | ``` 1047 | 1048 |
1050 | 1051 | `panic`と`recover`はエラーハンドリングではありません。 1052 | プログラムはnil参照などの回復不可能な状況が発生したとき以外は出すべきではありません。 1053 | ただプログラムの初期化時は例外です。 1054 | プログラムが開始するときに異常が起きた場合にはpanicを起こしてもよいでしょう。 1055 | 1056 | ```go 1057 | var _statusTemplate = template.Must(template.New("name").Parse("_statusHTML")) 1058 | ``` 1059 | 1060 | またテストでは、テストが失敗したことを示すためには`panic`ではなくて `t.Fatal` や `t.FailNow` を使うようにしましょう。 1061 | 1062 | 1063 | 1064 | 1065 | 1088 |
BadGood
1066 | 1067 | ```go 1068 | // func TestFoo(t *testing.T) 1069 | 1070 | f, err := ioutil.TempFile("", "test") 1071 | if err != nil { 1072 | panic("failed to set up test") 1073 | } 1074 | ``` 1075 | 1076 | 1077 | 1078 | ```go 1079 | // func TestFoo(t *testing.T) 1080 | 1081 | f, err := ioutil.TempFile("", "test") 1082 | if err != nil { 1083 | t.Fatal("failed to set up test") 1084 | } 1085 | ``` 1086 | 1087 |
1089 | 1090 | ## Use go.uber.org/atomic 1091 | 1092 | [sync/atomic](https://golang.org/pkg/sync/atomic)パッケージによるアトミック操作は`int32`や`int64`といった基本的な型を対象としているため、アトミックに操作すべき変数に対する読み出し・変更操作にアトミック操作を用いるということ(つまりsync/atomicパッケージの関数を使うこと自体)を容易に忘却させます。例では`int32`の変数に普通の読み出し操作を行ってしまっていますが、これはコンパイラの型チェック機構を素通ししてしまっているため潜在的に競合条件のあるコードをコンパイルできてしまっています。 1093 | 1094 | [go.uber.org/atomic](https://godoc.org/go.uber.org/atomic)は実際のデータの型を基底型として隠蔽することによりこれらのアトミック操作に対して型安全性を付与できます。これによって読み出し操作を行う方法はアトミックな操作に限定され、普通の読み出し操作はコンパイラの型チェックの機構によってコンパイル時にはじくことが可能となります。 1095 | また`sync/atomic`パッケージに加えて便利な`atomic.Bool`型も提供しています。 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1142 |
BadGood
1102 | 1103 | ```go 1104 | type foo struct { 1105 | running int32 // アトミックな操作が必要な変数 1106 | } 1107 | 1108 | func (f* foo) start() { 1109 | if atomic.SwapInt32(&f.running, 1) == 1 { 1110 | // すでに実行中 1111 | return 1112 | } 1113 | // Fooを開始 1114 | } 1115 | 1116 | func (f *foo) isRunning() bool { 1117 | return f.running == 1 // 競合条件! --> 別スレッドから実行されたatomic.SwapInt32による値の更新が見えないことが起こりうる 1118 | } 1119 | ``` 1120 | 1121 | 1122 | 1123 | ```go 1124 | type foo struct { 1125 | running atomic.Bool 1126 | } 1127 | 1128 | func (f *foo) start() { 1129 | if f.running.Swap(true) { 1130 | // すでに実行中 1131 | return 1132 | } 1133 | // Fooを開始 1134 | } 1135 | 1136 | func (f *foo) isRunning() bool { 1137 | return f.running.Load() // 読み出し操作がアトミックなため決定論的な振る舞いにになる(実はこの振る舞いはGoのメモリバリア指定がデフォルトSeqCstであることに依存するが、本項では深く触れない) 1138 | } 1139 | ``` 1140 | 1141 |
1143 | 1144 | ## Avoid Mutable Globals 1145 | グローバル変数を変更するのは避けましょう。 1146 | 代わりに依存関係の注入を使って構造体に持たせるようにしましょう。 1147 | 関数ポインタを他の値と同じように構造体にもたせます。 1148 | 1149 | 1150 | 1151 | 1152 | 1186 | 1218 |
BadGood
1153 | 1154 | ```go 1155 | // sign.go 1156 | 1157 | var _timeNow = time.Now 1158 | 1159 | func sign(msg string) string { 1160 | now := _timeNow() 1161 | return signWithTime(msg, now) 1162 | } 1163 | ``` 1164 | 1165 | 1166 | 1167 | ```go 1168 | // sign.go 1169 | 1170 | type signer struct { 1171 | now func() time.Time 1172 | } 1173 | 1174 | func newSigner() *signer { 1175 | return &signer{ 1176 | now: time.Now, 1177 | } 1178 | } 1179 | 1180 | func (s *signer) Sign(msg string) string { 1181 | now := s.now() 1182 | return signWithTime(msg, now) 1183 | } 1184 | ``` 1185 |
1187 | 1188 | ```go 1189 | // sign_test.go 1190 | 1191 | func TestSign(t *testing.T) { 1192 | oldTimeNow := _timeNow 1193 | _timeNow = func() time.Time { 1194 | return someFixedTime 1195 | } 1196 | defer func() { _timeNow = oldTimeNow }() 1197 | 1198 | assert.Equal(t, want, sign(give)) 1199 | } 1200 | ``` 1201 | 1202 | 1203 | 1204 | ```go 1205 | // sign_test.go 1206 | 1207 | func TestSigner(t *testing.T) { 1208 | s := newSigner() 1209 | s.now = func() time.Time { 1210 | return someFixedTime 1211 | } 1212 | 1213 | assert.Equal(t, want, s.Sign(give)) 1214 | } 1215 | ``` 1216 | 1217 |
1219 | 1220 | 1221 | ## Avoid Embedding Types in Public Structs 1222 | 型の埋め込みは実装の詳細を漏らし、型のブラッシュアップを阻害し、ドキュメントが曖昧になってしまいます。 1223 | 1224 | 共通の `AbstractList` を使って様々なリスト型を実装すると仮定します。 1225 | この場合に `AbstractList` を埋め込むことでリスト操作を実装するのはやめましょう。 1226 | 代わりにメソッドを再度定義してその中で `AbstractList` のメソッドを実装するようにしましょう。 1227 | 1228 | ```go 1229 | type AbstractList struct {} 1230 | 1231 | // Add adds an entity to the list. 1232 | func (l *AbstractList) Add(e Entity) { 1233 | // ... 1234 | } 1235 | 1236 | // Remove removes an entity from the list. 1237 | func (l *AbstractList) Remove(e Entity) { 1238 | // ... 1239 | } 1240 | ``` 1241 | 1242 | 1243 | 1244 | 1273 |
BadGood
1245 | 1246 | ```go 1247 | // ConcreteList is a list of entities. 1248 | type ConcreteList struct { 1249 | *AbstractList 1250 | } 1251 | ``` 1252 | 1253 | 1254 | 1255 | ```go 1256 | // ConcreteList is a list of entities. 1257 | type ConcreteList struct { 1258 | list *AbstractList 1259 | } 1260 | 1261 | // Add adds an entity to the list. 1262 | func (l *ConcreteList) Add(e Entity) { 1263 | return l.list.Add(e) 1264 | } 1265 | 1266 | // Remove removes an entity from the list. 1267 | func (l *ConcreteList) Remove(e Entity) { 1268 | return l.list.Remove(e) 1269 | } 1270 | ``` 1271 | 1272 |
1274 | 1275 | Go では継承が無い代わりに[埋め込み]( https://golang.org/doc/effective_go.html#embedding )を使えます。 1276 | 外部の型は暗黙的に埋め込まれた型のメソッドを実装しています。 1277 | これらのメソッドはデフォルトでは埋め込まれた型のインスタンスのメソッドになります。 1278 | 1279 | 構造体は埋め込んだ型と同じ名前のフィールドを作成します。 1280 | なので埋め込んだ型が公開されていたら、そのフィールドも公開されます。 1281 | 後方互換性を保つために、外側の型は埋め込んだ型を保持する必要があります。 1282 | 1283 | 埋め込みが必要な場面は殆どありません。 1284 | 多くは面倒なメソッドの移譲を書かずに済ませるために使われます。 1285 | 1286 | 構造体の代わりに AbstractList インタフェースを埋め込むこともできます。 1287 | これだと開発者に将来的な自由度をもたせることができます。 1288 | しかし、抽象的な実装に依存して実装の詳細が漏れるという問題は解決されません。 1289 | 1290 | 1291 | 1292 | 1293 | 1329 |
BadGood
1294 | 1295 | ```go 1296 | // AbstractList is a generalized implementation 1297 | // for various kinds of lists of entities. 1298 | type AbstractList interface { 1299 | Add(Entity) 1300 | Remove(Entity) 1301 | } 1302 | 1303 | // ConcreteList is a list of entities. 1304 | type ConcreteList struct { 1305 | AbstractList 1306 | } 1307 | ``` 1308 | 1309 | 1310 | 1311 | ```go 1312 | // ConcreteList is a list of entities. 1313 | type ConcreteList struct { 1314 | list *AbstractList 1315 | } 1316 | 1317 | // Add adds an entity to the list. 1318 | func (l *ConcreteList) Add(e Entity) { 1319 | return l.list.Add(e) 1320 | } 1321 | 1322 | // Remove removes an entity from the list. 1323 | func (l *ConcreteList) Remove(e Entity) { 1324 | return l.list.Remove(e) 1325 | } 1326 | ``` 1327 | 1328 |
1330 | 1331 | 構造体の埋め込みでもインタフェースの埋め込みでも、将来的な型の変更に制限がかかります。 1332 | 1333 | * 埋め込まれたインタフェースにメソッドを追加することは破壊的変更になります 1334 | * 埋め込まれた構造体からメソッドを削除すると破壊的変更になります 1335 | * 埋め込まれた型を削除することは破壊的変更になります 1336 | * 埋め込まれた型を同じインタフェースを実装した別の型に差し替える場合も破壊的変更になります 1337 | 1338 | 埋め込みの代わりに同じメソッドを書くのは面倒ですが、その分実装の詳細を外側から隠すことができます。 1339 | 実装の詳細をそのメソッドが持つことで内部での変更がしやすくなります。 1340 | 実装がすぐに見えるので、Listの詳細をさらに見に行く必要がなくなります。 1341 | 1342 | ## Avoid Using Built-in Names 1343 | 1344 | Go の[言語仕様]( https://golang.org/ref/spec )ではいくつかのビルトイン、または[定義済み識別子]( https://golang.org/ref/spec#Predeclared_identifiers )があります。これらは Go のプログラム内で識別子として使うべきではありません。 1345 | 1346 | 状況にもよりますが、これらの識別子を再利用すると、元の識別子がレキシカルスコープ内で隠蔽されるか、元のコードを混乱させます。 1347 | コンパイラがエラーを出して気づく場合もありますが、最悪の場合は grep などでは発見困難な潜在的バグを起こす可能性があります。 1348 | 1349 | 1350 | 1351 | 1352 | 1379 | 1420 |
BadGood
1353 | 1354 | ```go 1355 | var error string 1356 | // `error` はビルトインの型を隠す 1357 | 1358 | // or 1359 | 1360 | func handleErrorMessage(error string) { 1361 | // `error` はビルトインの型を隠す 1362 | } 1363 | ``` 1364 | 1365 | 1366 | 1367 | ```go 1368 | var errorMessage string 1369 | // `error` はビルトインの型のまま 1370 | 1371 | // or 1372 | 1373 | func handleErrorMessage(msg string) { 1374 | // `error` はビルトインの型のまま 1375 | } 1376 | ``` 1377 | 1378 |
1380 | 1381 | ```go 1382 | type Foo struct { 1383 | // これらのフィールドは 1384 | // 技術的にはシャドウイングを引き起こしませんが、 1385 | // `error` や `string` という文字列は曖昧です。 1386 | error error 1387 | string string 1388 | } 1389 | 1390 | func (f Foo) Error() error { 1391 | // `error` と `f.error` は見た目が似ている 1392 | return f.error 1393 | } 1394 | 1395 | func (f Foo) String() string { 1396 | // `string` と `f.string` は見た目が似ている 1397 | return f.string 1398 | } 1399 | ``` 1400 | 1401 | 1402 | 1403 | ```go 1404 | type Foo struct { 1405 | // `error` や `string` という文字列は明確に型名を指します 1406 | err error 1407 | str string 1408 | } 1409 | 1410 | func (f Foo) Error() error { 1411 | return f.err 1412 | } 1413 | 1414 | func (f Foo) String() string { 1415 | return f.str 1416 | } 1417 | ``` 1418 | 1419 |
1421 | 1422 | 宣言済み識別子名をローカルの識別子に使ってもコンパイラはエラーを出さないことに注意してください。ですが `go vet` などのツールはこれらのシャドウイングを正しく見つけることができます。 1423 | 1424 | ## Avoid `init()` 1425 | 1426 | できるなら `init()` を使うのは避けましょう。次のケースの場合は避けようがなかったり、推奨されます。 1427 | 1428 | - 実行環境や呼び出し方に関係なく、決定的である場合 1429 | - 他の `init()` 関数の実行順や副作用に影響されない場合。`init()` の順序はよく知られていますが、コードが変更され、 `init()` 関数間の関係によってはコードが脆弱になり、エラーが発生しやすくなります 1430 | - グローバルな情報や、環境変数、ワーキングディレクトリ、プログラムの引数や入力にアクセスしたり、操作しない場合 1431 | - ファイルシステム、ネットワーク、システムコールの操作をしない場合 1432 | 1433 | コードがこれらの要件を満たせない場合、 `main()` 関数か、プログラムのライフサイクルの一部でヘルパー関数として呼び出すか、`main()` 関数で直接呼び出す必要があります。 1434 | 特にライブラリなど他のプログラムで使われることを想定したコードの場合は特に決定的であることに注意し、 "init magic" を引き起こさないように注意しましょう。 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1475 | 1521 |
BadGood
1441 | 1442 | ```go 1443 | type Foo struct { 1444 | // ... 1445 | } 1446 | 1447 | var _defaultFoo Foo 1448 | 1449 | func init() { 1450 | _defaultFoo = Foo{ 1451 | // ... 1452 | } 1453 | } 1454 | ``` 1455 | 1456 | 1457 | 1458 | ```go 1459 | var _defaultFoo = Foo{ 1460 | // ... 1461 | } 1462 | 1463 | // もしくはテスト可能性に配慮してこのようにします 1464 | 1465 | var _defaultFoo = defaultFoo() 1466 | 1467 | func defaultFoo() Foo { 1468 | return Foo{ 1469 | // ... 1470 | } 1471 | } 1472 | ``` 1473 | 1474 |
1476 | 1477 | ```go 1478 | type Config struct { 1479 | // ... 1480 | } 1481 | 1482 | var _config Config 1483 | 1484 | func init() { 1485 | // 悪い例: 現在のディレクトリに依存している 1486 | cwd, _ := os.Getwd() 1487 | 1488 | // 悪い例: I/O 1489 | raw, _ := os.ReadFile( 1490 | path.Join(cwd, "config", "config.yaml"), 1491 | ) 1492 | 1493 | yaml.Unmarshal(raw, &_config) 1494 | } 1495 | ``` 1496 | 1497 | 1498 | 1499 | ```go 1500 | type Config struct { 1501 | // ... 1502 | } 1503 | 1504 | func loadConfig() Config { 1505 | cwd, err := os.Getwd() 1506 | // handle err 1507 | 1508 | raw, err := os.ReadFile( 1509 | path.Join(cwd, "config", "config.yaml"), 1510 | ) 1511 | // handle err 1512 | 1513 | var config Config 1514 | yaml.Unmarshal(raw, &config) 1515 | 1516 | return config 1517 | } 1518 | ``` 1519 | 1520 |
1522 | 1523 | これらを考慮すると、次のような状況では `init()` が望ましかったり必要になる可能性があります。 1524 | 1525 | - ただの代入では表現できない複雑な式 1526 | - `database/sql` の登録や、エンコーディングタイプの登録など、プラグイン的に使うフック 1527 | - [Google Cloud Functions]( https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations ) などの決定的事前処理の最適化 1528 | 1529 | ## Exit in Main 1530 | 1531 | Go のプログラムでは即時終了するために [`os.Exit`]( https://golang.org/pkg/os/#Exit ) や [`log.Fatal*`]( https://golang.org/pkg/log/#Fatal ) を使います。`panic()` を使うのは良い方法ではありません [don't panic](#dont-panic)を読んでください。 1532 | `os.Exit` や `log.Fatal*` を読んでいいのは `main()` 関数だけです。他の関数ではエラーを返して失敗を通知しましょう。 1533 | 1534 | 1535 | 1536 | 1537 | 1587 |
BadGood
1538 | 1539 | ```go 1540 | func main() { 1541 | body := readFile(path) 1542 | fmt.Println(body) 1543 | } 1544 | 1545 | func readFile(path string) string { 1546 | f, err := os.Open(path) 1547 | if err != nil { 1548 | log.Fatal(err) 1549 | } 1550 | 1551 | b, err := io.ReadAll(f) 1552 | if err != nil { 1553 | log.Fatal(err) 1554 | } 1555 | 1556 | return string(b) 1557 | } 1558 | ``` 1559 | 1560 | 1561 | 1562 | ```go 1563 | func main() { 1564 | body, err := readFile(path) 1565 | if err != nil { 1566 | log.Fatal(err) 1567 | } 1568 | fmt.Println(body) 1569 | } 1570 | 1571 | func readFile(path string) (string, error) { 1572 | f, err := os.Open(path) 1573 | if err != nil { 1574 | return "", err 1575 | } 1576 | 1577 | b, err := io.ReadAll(f) 1578 | if err != nil { 1579 | return "", err 1580 | } 1581 | 1582 | return string(b), nil 1583 | } 1584 | ``` 1585 | 1586 |
1588 | 1589 | 根拠: `exit` する関数が複数あるプログラムにはいくつかの問題があります。 1590 | 1591 | - 明確でない制御フロー: どの関数もプログラムを強制終了できるので、制御フローを推論するのが難しくなります。 1592 | - テストが難しくなる: 強制終了するプログラムはテストで呼び出されたときも終了します。これはその関数をテストするのも難しくなりますし、`go test` でテストされるはずだった他のテストがスキップされる危険もあります。 1593 | - 後処理のスキップ: プログラムが強制終了されたとき、`defer` で終了時に実行する予定だった関数がスキップされます。これは重要な後処理をスキップする危険があります。 1594 | 1595 | ### Exit Once 1596 | 可能なら、 `os.Exit` か `log.Fatal` を `main()` 関数で **一度だけ** 呼ぶのが好ましいです。もしプログラムを停止する失敗のシナリオがいくつかある場合、そのロジックは別の関数にしてエラーを返しましょう。 1597 | 1598 | こうすると、 `main()` 関数を短くすることができますし、重要なビジネスロジックを分離してテストしやすくすることができます。 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | 1668 |
BadGood
1605 | 1606 | ```go 1607 | package main 1608 | 1609 | func main() { 1610 | args := os.Args[1:] 1611 | if len(args) != 1 { 1612 | log.Fatal("missing file") 1613 | } 1614 | name := args[0] 1615 | 1616 | f, err := os.Open(name) 1617 | if err != nil { 1618 | log.Fatal(err) 1619 | } 1620 | defer f.Close() 1621 | 1622 | // もしここで log.Fatal を呼ぶと、 1623 | // f.Close は実行されません。 1624 | 1625 | b, err := io.ReadAll(f) 1626 | if err != nil { 1627 | log.Fatal(err) 1628 | } 1629 | 1630 | // ... 1631 | } 1632 | ``` 1633 | 1634 | 1635 | 1636 | ```go 1637 | package main 1638 | 1639 | func main() { 1640 | if err := run(); err != nil { 1641 | log.Fatal(err) 1642 | } 1643 | } 1644 | 1645 | func run() error { 1646 | args := os.Args[1:] 1647 | if len(args) != 1 { 1648 | return errors.New("missing file") 1649 | } 1650 | name := args[0] 1651 | 1652 | f, err := os.Open(name) 1653 | if err != nil { 1654 | return err 1655 | } 1656 | defer f.Close() 1657 | 1658 | b, err := io.ReadAll(f) 1659 | if err != nil { 1660 | return err 1661 | } 1662 | 1663 | // ... 1664 | } 1665 | ``` 1666 | 1667 |
1669 | 1670 | ## Use field tags in marshaled structs 1671 | JSON や YAML あるいは他のタグを使ったフィールド名をサポートするフォーマットに変換する場合、関連するタグを使ってアノテーションをつけましょう。 1672 | 1673 | 1674 | 1675 | 1676 | 1677 | 1707 |
BadGood
1678 | 1679 | ```go 1680 | type Stock struct { 1681 | Price int 1682 | Name string 1683 | } 1684 | 1685 | bytes, err := json.Marshal(Stock{ 1686 | Price: 137, 1687 | Name: "UBER", 1688 | }) 1689 | ``` 1690 | 1691 | 1692 | 1693 | ```go 1694 | type Stock struct { 1695 | Price int `json:"price"` 1696 | Name string `json:"name"` 1697 | // 安全に Name フィールドを name に変換できる 1698 | } 1699 | 1700 | bytes, err := json.Marshal(Stock{ 1701 | Price: 137, 1702 | Name: "UBER", 1703 | }) 1704 | ``` 1705 | 1706 |
1708 | 1709 | 根拠: 構造体をシリアライズした形式は異なるシステムをつなぐ約束事です。 1710 | フィールド名を含む構造体のシリアライズした形式が変わってしまうと、この約束事が破れてしまいます。 1711 | タグを使ってフィールド名を指定するとこの約束事がより厳密になり、リファクタリングやフィールドのリネームで不意に壊れてしまうことを防ぐことができます。 1712 | 1713 | ## Don't fire-and-forget goroutines 1714 | 1715 | ゴルーチンは軽量ですが、コストはかかります。少なくとも、スタックのメモリとスケジュールされたCPUを使います。 1716 | 典型的な使い方をする限りコストは小さいですが、ライフタイムを考えずに大量に作り出すと大きなパフォーマンス問題を引き起こします。 1717 | ライフタイムが管理されてないゴルーチンは、ガベージコレクションの邪魔になったり、使用されなくなったリソースを保持し続けるなどの問題も引き起こす可能性があります。 1718 | 1719 | なので、絶対に本番コードでゴルーチンをリークさせないようにしましょう。 [go.uber.org/goleak]( https://pkg.go.dev/go.uber.org/goleak ) でゴルーチンを使うところでリークさせてないかテストしましょう。 1720 | 1721 | 一般的に、全てのゴルーチンはどちらかの方法を持っている必要があります。 1722 | 1723 | - 停止する時間が予測できる 1724 | - 停止すべきゴルーチンに通知する方法がある 1725 | 1726 | どちらのケースでも、処理をブロックしてゴルーチンの終了を待つコードも無ければいけません。 1727 | 1728 | 1729 | 例: 1730 | 1731 | 1732 | 1733 | 1772 | 1781 |
BadGood
1734 | 1735 | ```go 1736 | go func() { 1737 | for { 1738 | flush() 1739 | time.Sleep(delay) 1740 | } 1741 | }() 1742 | ``` 1743 | 1744 | 1745 | 1746 | ```go 1747 | var ( 1748 | stop = make(chan struct{}) // ゴルーチンに停止を伝える 1749 | done = make(chan struct{}) // ゴルーチンが停止したことを伝える 1750 | ) 1751 | go func() { 1752 | defer close(done) 1753 | 1754 | ticker := time.NewTicker(delay) 1755 | defer ticker.Stop() 1756 | for { 1757 | select { 1758 | case <-ticker.C: 1759 | flush() 1760 | case <-stop: 1761 | return 1762 | } 1763 | } 1764 | }() 1765 | 1766 | // Elsewhere... 1767 | close(stop) // ゴルーチンに停止シグナルを送る 1768 | <-done // そしてゴルーチンが止まるのを待つ 1769 | ``` 1770 | 1771 |
1773 | 1774 | ゴルーチンを止める方法は無い。プログラムが終了するまで残り続ける 1775 | 1776 | 1777 | 1778 | ゴルーチンは `close(stop)` で停止できる。そして `<-done` で待つこともできる。 1779 | 1780 |
1782 | 1783 | ### Wait for goroutines to exit 1784 | 1785 | システムによって起動されたゴルーチンが与えられた場合、ゴルーチンの終了を待つ方法を用意する必要があります。次の2つがよく使われる方法です。 1786 | 1787 | * `sync.WaitGroup` を使います。終了を待つべきゴルーチンが複数ある場合、こちらを使う 1788 | 1789 | ```go 1790 | var wg sync.WaitGroup 1791 | for i := 0; i < N; i++ { 1792 | wg.Add(1) 1793 | go func() { 1794 | defer wg.Done() 1795 | // ... 1796 | }() 1797 | } 1798 | 1799 | // 全ての終了を待つ 1800 | wg.Wait() 1801 | ``` 1802 | 1803 | * 別の `chan struct{}` を作り、ゴルーチンが終了したとき `close` します。待つべきゴルーチンが1つだけのときはこちらを使いましょう 1804 | 1805 | ```go 1806 | done := make(chan struct{}) 1807 | go func() { 1808 | defer close(done) 1809 | // ... 1810 | }() 1811 | 1812 | // ここでゴルーチンの終了を待つ 1813 | <-done 1814 | ``` 1815 | 1816 | ### No goroutines in `init()` 1817 | 1818 | `init()` 関数ではゴルーチンを起動するのを避けましょう。 [Avoid init](#avoid-init) も参照してください。 1819 | 1820 | もしパッケージがバックグラウンドで動くゴルーチンを作る必要があるなら、ゴルーチンのライフタイムを管理するオブジェクトを作りそれを公開しましょう。 1821 | そのオブジェクトは `Close`、`Stop`、`Shutdown` など、バックグラウンドのゴルーチンを停止し、その終了を待つメソッドを提供しましょう。 1822 | 1823 | 1824 | 1825 | 1826 | 1872 | 1883 |
BadGood
1827 | 1828 | ```go 1829 | func init() { 1830 | go doWork() 1831 | } 1832 | 1833 | func doWork() { 1834 | for { 1835 | // ... 1836 | } 1837 | } 1838 | ``` 1839 | 1840 | 1841 | 1842 | ```go 1843 | type Worker struct{ /* ... */ } 1844 | 1845 | func NewWorker(...) *Worker { 1846 | w := &Worker{ 1847 | stop: make(chan struct{}), 1848 | done: make(chan struct{}), 1849 | // ... 1850 | } 1851 | go w.doWork() 1852 | } 1853 | 1854 | func (w *Worker) doWork() { 1855 | defer close(w.done) 1856 | for { 1857 | // ... 1858 | case <-w.stop: 1859 | return 1860 | } 1861 | } 1862 | 1863 | // Shutdown はワーカーに終了を伝え、 1864 | // ワーカーが終了するのを待ちます。 1865 | func (w *Worker) Shutdown() { 1866 | close(w.stop) 1867 | <-w.done 1868 | } 1869 | ``` 1870 | 1871 |
1873 | 1874 | ユーザーがこのパッケージを公開すると、ゴルーチンを無条件に作成し、止める方法はありません。 1875 | 1876 | 1877 | 1878 | ユーザーがリクエストすると、ワーカーを起動します。`Shutdown` メソッドで、ワーカーを停止し使っているリソースを開放できる手段も提供しています。 1879 | 1880 | [Wait for goroutines to exit]( #wait-for-gorutines-to-exit ) でも話したように、ワーカーが複数ゴルーチンを使う場合は `WaitGroup` を使いましょう。 1881 | 1882 |
1884 | 1885 | 1886 | # Performance 1887 | パフォーマンスガイドラインは特によく実行される箇所にのみ適用されます。 1888 | 1889 | ## Prefer strconv over fmt 1890 | 数字と文字列を単に変換する場合、`fmt` パッケージよりも `strconv` パッケージのほうが高速に実行されます。 1891 | 1892 | 1893 | 1894 | 1895 | 1912 | 1925 |
BadGood
1896 | 1897 | ```go 1898 | for i := 0; i < b.N; i++ { 1899 | s := fmt.Sprint(rand.Int()) 1900 | } 1901 | ``` 1902 | 1903 | 1904 | 1905 | ```go 1906 | for i := 0; i < b.N; i++ { 1907 | s := strconv.Itoa(rand.Int()) 1908 | } 1909 | ``` 1910 | 1911 |
1913 | 1914 | ``` 1915 | BenchmarkFmtSprint-4 143 ns/op 2 allocs/op 1916 | ``` 1917 | 1918 | 1919 | 1920 | ``` 1921 | BenchmarkStrconv-4 64.2 ns/op 1 allocs/op 1922 | ``` 1923 | 1924 |
1926 | 1927 | ## Avoid string-to-byte conversion 1928 | 固定の文字列からバイト列を何度も生成するのは避けましょう。 1929 | 代わりに変数に格納してそれを使うようにしましょう。 1930 | 1931 | 1932 | 1933 | 1934 | 1952 | 1965 |
BadGood
1935 | 1936 | ```go 1937 | for i := 0; i < b.N; i++ { 1938 | w.Write([]byte("Hello world")) 1939 | } 1940 | ``` 1941 | 1942 | 1943 | 1944 | ```go 1945 | data := []byte("Hello world") 1946 | for i := 0; i < b.N; i++ { 1947 | w.Write(data) 1948 | } 1949 | ``` 1950 | 1951 |
1953 | 1954 | ``` 1955 | BenchmarkBad-4 50000000 22.2 ns/op 1956 | ``` 1957 | 1958 | 1959 | 1960 | ``` 1961 | BenchmarkGood-4 500000000 3.25 ns/op 1962 | ``` 1963 | 1964 |
1966 | 1967 | ## Prefer Specifying Map Capacity Hints 1968 | スライスやmapの容量のヒントが事前にある場合は初期化時に次のように設定しましょう。 1969 | 1970 | ```go 1971 | make(map[T1]T2, hint) 1972 | ``` 1973 | 1974 | `make()` の引数にキャパシティを渡すと、初期化時に適切なサイズにしようとします。 1975 | なので要素をマップに追加する際にアロケーションの回数を減らすことができます。 1976 | ただし、キャパシティのヒントは必ずしも保証されるものではありません。 1977 | もし事前にキャパシティを渡していても、要素の追加時にアロケーションが発生する場合もあります。 1978 | 1979 | 1980 | 1981 | 1982 | 2003 | 2014 |
BadGood
1983 | 1984 | ```go 1985 | m := make(map[string]os.FileInfo) 1986 | files, _ := ioutil.ReadDir("./files") 1987 | for _, f := range files { 1988 | m[f.Name()] = f 1989 | } 1990 | ``` 1991 | 1992 | 1993 | 1994 | ```go 1995 | files, _ := ioutil.ReadDir("./files") 1996 | m := make(map[string]os.FileInfo, len(files)) 1997 | for _, f := range files { 1998 | m[f.Name()] = f 1999 | } 2000 | ``` 2001 | 2002 |
2004 | 2005 | `m` は初期化時にサイズのヒントが与えられませんでした。 2006 | そのため余計にアロケーションが発生する可能性があります。 2007 | 2008 | 2009 | 2010 | `m` にはサイズのヒントが与えられています。 2011 | 要素の追加時にアロケーションの回数を押さえられます。 2012 | 2013 |
2015 | 2016 | # Style 2017 | 2018 | ## Avoid overly long lines 2019 | 横にスクロールしたり、たくさん首をふるような長過ぎるコードは避けましょう。 2020 | 2021 | 横幅は99文字を推奨しています。書く側はこれを超えると改行したほうが良いですが、絶対ではありません。 2022 | コードが超えても問題ありません。 2023 | 2024 | ## Be Consistent 2025 | このガイドラインの一部は客観的に評価することができます。 2026 | ですが状況や文脈に依存する主観的なものもあります。 2027 | 2028 | ですが何よりも重要なことは一貫性を保つことです。 2029 | 2030 | 一貫性のあるコードは保守しやすく、説明しやすく、読むときのオーバーヘッドも減らせます。 2031 | 更に新しい規則やバグへの修正が非常に簡単になります。 2032 | 2033 | 逆に、1つのコードベース内に複数の異なったりバッティングしているスタイルがあると、メンテナンスのオーバーヘッドや、不確実なコード、認知的不協和が発生します。 2034 | これらの全てが開発速度の低下、苦痛なコードレビューを誘発し、更にバグを発生させます。 2035 | 2036 | このガイドラインにある項目を自分たちのコードに適用する場合、パッケージもしくは更に大きな単位で適用することを勧めます。 2037 | サブパッケージレベルで適用することは同じコードベースに複数のスタイルを当てはめることになるため、先程述べた悪いパターンに当てはまっています。 2038 | 2039 | ## Group Similar Declarations 2040 | Go は似たような宣言をグループにまとめることができます。 2041 | 2042 | 2043 | 2044 | 2045 | 2062 |
BadGood
2046 | 2047 | ```go 2048 | import "a" 2049 | import "b" 2050 | ``` 2051 | 2052 | 2053 | 2054 | ```go 2055 | import ( 2056 | "a" 2057 | "b" 2058 | ) 2059 | ``` 2060 | 2061 |
2063 | 2064 | これはパッケージ定数やパッケージ変数、型定義などにも利用できます。 2065 | 2066 | 2067 | 2068 | 2069 | 2107 |
BadGood
2070 | 2071 | ```go 2072 | 2073 | const a = 1 2074 | const b = 2 2075 | 2076 | 2077 | 2078 | var a = 1 2079 | var b = 2 2080 | 2081 | 2082 | 2083 | type Area float64 2084 | type Volume float64 2085 | ``` 2086 | 2087 | 2088 | 2089 | ```go 2090 | const ( 2091 | a = 1 2092 | b = 2 2093 | ) 2094 | 2095 | var ( 2096 | a = 1 2097 | b = 2 2098 | ) 2099 | 2100 | type ( 2101 | Area float64 2102 | Volume float64 2103 | ) 2104 | ``` 2105 | 2106 |
2108 | 2109 | 関係が近いものだけをグループ化しましょう。 2110 | 関係ないものまでグループ化するのは避けましょう。 2111 | 2112 | 2113 | 2114 | 2115 | 2143 |
BadGood
2116 | 2117 | ```go 2118 | type Operation int 2119 | 2120 | const ( 2121 | Add Operation = iota + 1 2122 | Subtract 2123 | Multiply 2124 | ENV_VAR = "MY_ENV" 2125 | ) 2126 | ``` 2127 | 2128 | 2129 | 2130 | ```go 2131 | type Operation int 2132 | 2133 | const ( 2134 | Add Operation = iota + 1 2135 | Subtract 2136 | Multiply 2137 | ) 2138 | 2139 | const ENV_VAR = "MY_ENV" 2140 | ``` 2141 | 2142 |
2144 | 2145 | グループ化を使う場所に制限は無いので、関数内でも使うことができます。 2146 | 2147 | 2148 | 2149 | 2150 | 2177 |
BadGood
2151 | 2152 | ```go 2153 | func f() string { 2154 | var red = color.New(0xff0000) 2155 | var green = color.New(0x00ff00) 2156 | var blue = color.New(0x0000ff) 2157 | 2158 | ... 2159 | } 2160 | ``` 2161 | 2162 | 2163 | 2164 | ```go 2165 | func f() string { 2166 | var ( 2167 | red = color.New(0xff0000) 2168 | green = color.New(0x00ff00) 2169 | blue = color.New(0x0000ff) 2170 | ) 2171 | 2172 | ... 2173 | } 2174 | ``` 2175 | 2176 |
2178 | 2179 | ## Import Group Ordering 2180 | import のグループは次の2つに分けるべきです。 2181 | 2182 | 1. 標準パッケージ 2183 | 2. それ以外 2184 | 2185 | goimports がデフォルトで適用してくれます。 2186 | 2187 | 2188 | 2189 | 2190 | 2214 |
BadGood
2191 | 2192 | ```go 2193 | import ( 2194 | "fmt" 2195 | "os" 2196 | "go.uber.org/atomic" 2197 | "golang.org/x/sync/errgroup" 2198 | ) 2199 | ``` 2200 | 2201 | 2202 | 2203 | ```go 2204 | import ( 2205 | "fmt" 2206 | "os" 2207 | 2208 | "go.uber.org/atomic" 2209 | "golang.org/x/sync/errgroup" 2210 | ) 2211 | ``` 2212 | 2213 |
2215 | 2216 | ## Package Names 2217 | パッケージ名をつける場合、以下のルールに従いましょう。 2218 | 2219 | * 全て小文字で大文字やアンダースコアを使わない 2220 | * ほとんどの呼び出し側が名前付きインポートをする必要がないようにする 2221 | * 短く簡潔にすること。全ての呼び出し側で識別されることを意識してください 2222 | * 複数形にしないこと。`net/urls`ではなく`net/url`です 2223 | * "common"、"util"、"shared"、"lib"などを使わないこと。これらはなんの情報もない名前です。 2224 | 2225 | [Package Names]( https://blog.golang.org/package-names )や[Style guideline for Go packages]( https://rakyll.org/style-packages/ )も参考にしてください。 2226 | 2227 | ## Function Names 2228 | 関数名にはGoコミュニティの規則である[MixedCaps]( https://golang.org/doc/effective_go.html#mixed-caps )に従います。 2229 | 例外はテスト関数です。 2230 | `TestMyFunction_WhatIsBeingTested`のようにテストの目的を示すためにアンダースコアを使って分割します。 2231 | 2232 | ## Import Aliasing 2233 | インポートエイリアスはパッケージ名とパッケージパスの末尾が一致していない場合に利用します。 2234 | 2235 | ```go 2236 | import ( 2237 | "net/http" 2238 | 2239 | client "example.com/client-go" 2240 | trace "example.com/trace/v2" 2241 | ) 2242 | ``` 2243 | 2244 | 他にもインポートするパッケージの名前がバッティングした場合には使います。 2245 | それ以外の場合は使わないようにしましょう。 2246 | 2247 | 2248 | 2249 | 2250 | 2275 |
BadGood
2251 | 2252 | ```go 2253 | import ( 2254 | "fmt" 2255 | "os" 2256 | 2257 | 2258 | nettrace "golang.net/x/trace" 2259 | ) 2260 | ``` 2261 | 2262 | 2263 | 2264 | ```go 2265 | import ( 2266 | "fmt" 2267 | "os" 2268 | "runtime/trace" 2269 | 2270 | nettrace "golang.net/x/trace" 2271 | ) 2272 | ``` 2273 | 2274 |
2276 | 2277 | ## Function Grouping and Ordering 2278 | 2279 | * 関数は呼び出される順番におおまかにソートされるべきです 2280 | * 関数はレシーバーごとにまとめられているべきです。 2281 | 2282 | なので、`struct`、`const`、`var`の次にパッケージ外に公開されている関数が来るべきです。 2283 | 2284 | `newXYZ()`や`NewXYZ()`は型が定義されたあと、他のメソッドの前に定義されている必要があります。 2285 | 2286 | 関数はレシーバーごとにまとめられているので、ユーティリティな関数はファイルの最後の方に出てくるはずです。 2287 | 2288 | 2289 | 2290 | 2291 | 2292 | 2329 |
BadGood
2293 | 2294 | ```go 2295 | func (s *something) Cost() { 2296 | return calcCost(s.weights) 2297 | } 2298 | 2299 | type something struct{ ... } 2300 | 2301 | func calcCost(n []int) int {...} 2302 | 2303 | func (s *something) Stop() {...} 2304 | 2305 | func newSomething() *something { 2306 | return &something{} 2307 | } 2308 | ``` 2309 | 2310 | 2311 | 2312 | ```go 2313 | type something struct{ ... } 2314 | 2315 | func newSomething() *something { 2316 | return &something{} 2317 | } 2318 | 2319 | func (s *something) Cost() { 2320 | return calcCost(s.weights) 2321 | } 2322 | 2323 | func (s *something) Stop() {...} 2324 | 2325 | func calcCost(n []int) int {...} 2326 | ``` 2327 | 2328 |
2330 | 2331 | ## Reduce Nesting 2332 | エラーや特殊ケースなどは早めにハンドリングして`return`したりループ内では`continue`や`break`してネストが浅いコードを目指しましょう。 2333 | ネストが深いコードを減らしていきましょう。 2334 | 2335 | 2336 | 2337 | 2338 | 2373 |
BadGood
2339 | 2340 | ```go 2341 | for _, v := range data { 2342 | if v.F1 == 1 { 2343 | v = process(v) 2344 | if err := v.Call(); err == nil { 2345 | v.Send() 2346 | } else { 2347 | return err 2348 | } 2349 | } else { 2350 | log.Printf("Invalid v: %v", v) 2351 | } 2352 | } 2353 | ``` 2354 | 2355 | 2356 | 2357 | ```go 2358 | for _, v := range data { 2359 | if v.F1 != 1 { 2360 | log.Printf("Invalid v: %v", v) 2361 | continue 2362 | } 2363 | 2364 | v = process(v) 2365 | if err := v.Call(); err != nil { 2366 | return err 2367 | } 2368 | v.Send() 2369 | } 2370 | ``` 2371 | 2372 |
2374 | 2375 | ## Unnecessary Else 2376 | if-else のどちらでも変数に代入する場合、条件に一致した場合に上書きするようにしましょう。 2377 | 2378 | 2379 | 2380 | 2381 | 2402 |
BadGood
2382 | 2383 | ```go 2384 | var a int 2385 | if b { 2386 | a = 100 2387 | } else { 2388 | a = 10 2389 | } 2390 | ``` 2391 | 2392 | 2393 | 2394 | ```go 2395 | a := 10 2396 | if b { 2397 | a = 100 2398 | } 2399 | ``` 2400 | 2401 |
2403 | 2404 | ## Top-level Variable Declarations 2405 | パッケージ変数で式と同じ型なら型名を指定しないようにしましょう。 2406 | 2407 | 2408 | 2409 | 2410 | 2429 |
BadGood
2411 | 2412 | ```go 2413 | var _s string = F() 2414 | 2415 | func F() string { return "A" } 2416 | ``` 2417 | 2418 | 2419 | 2420 | ```go 2421 | var _s = F() 2422 | // Since F already states that it returns a string, we don't need to specify 2423 | // the type again. 2424 | 2425 | func F() string { return "A" } 2426 | ``` 2427 | 2428 |
2430 | 2431 | 式の型と合わない場合は明示するようにしましょう。 2432 | 2433 | ```go 2434 | type myError struct{} 2435 | 2436 | func (myError) Error() string { return "error" } 2437 | 2438 | func F() myError { return myError{} } 2439 | 2440 | var _e error = F() 2441 | // F は myError 型を返すが私達は error 型が欲しい 2442 | ``` 2443 | 2444 | ## Prefix Unexported Globals with _ 2445 | 公開されてないtop-levelの`var`や`const`の名前には最初にアンダースコアをつけることでより内部向けてあることが明確になります。 2446 | ただ`err`で始まる変数名は例外です。 2447 | 理由としてtop-levelの変数のスコープはそのパッケージ全体です。一般的な名前を使うと別のファイルで間違った値を使ってしまうことになります。 2448 | 2449 | 2450 | 2451 | 2452 | 2486 |
BadGood
2453 | 2454 | ```go 2455 | // foo.go 2456 | 2457 | const ( 2458 | defaultPort = 8080 2459 | defaultUser = "user" 2460 | ) 2461 | 2462 | // bar.go 2463 | 2464 | func Bar() { 2465 | defaultPort := 9090 2466 | ... 2467 | fmt.Println("Default port", defaultPort) 2468 | 2469 | // We will not see a compile error if the first line of 2470 | // Bar() is deleted. 2471 | } 2472 | ``` 2473 | 2474 | 2475 | 2476 | ```go 2477 | // foo.go 2478 | 2479 | const ( 2480 | _defaultPort = 8080 2481 | _defaultUser = "user" 2482 | ) 2483 | ``` 2484 | 2485 |
2487 | 2488 | ## Embedding in Structs 2489 | 2490 | 埋め込まれた型は構造体の定義の最初に置くべきです。 2491 | また、通常のフィールドと区別するために1行開ける必要があります。 2492 | 2493 | 2494 | 2495 | 2496 | 2516 |
BadGood
2497 | 2498 | ```go 2499 | type Client struct { 2500 | version int 2501 | http.Client 2502 | } 2503 | ``` 2504 | 2505 | 2506 | 2507 | ```go 2508 | type Client struct { 2509 | http.Client 2510 | 2511 | version int 2512 | } 2513 | ``` 2514 | 2515 |
2517 | 2518 | 埋め込みは適切な方法で機能を追加、拡張できる具体的なメリットがある場合に使いましょう。 2519 | ユーザーに悪影響を与えずに行う必要があります。[Avoid Embedding Types in Public Structs]( #avoid-embedding-types-in-public-structs ) も参照しましょう。 2520 | 2521 | 例外: Mutex は埋め込むべきではありません。公開されないフィールドとして使いましょう。[Zero-value Mutexes are Valid]( #zero-value-mutexes-are-valid ) も参照しましょう。 2522 | 2523 | 埋め込みは次のことをすべきではありません。: 2524 | 2525 | - 見た目や、手軽さを重視して使うこと 2526 | - 埋め込まれた型が使いにくくなること 2527 | - 埋め込まれた型のゼロ値に影響すること。もし埋め込まれた型が便利なゼロ値を持っているなら、埋め込まれた後にもそれが維持されるようにしなければいけません。 2528 | - 埋め込まれた副作用として関係ないメソッドやフィールドが公開されてしまうこと 2529 | - 公開してない型を公開してしまうこと 2530 | - 埋め込まれた型のコピーに影響が出ること 2531 | - 埋め込まれた型の API や、意味上の型が変わってしまうこと 2532 | - 非標準の形式で埋め込むこと 2533 | - 埋め込まれる型の実装の詳細を公開すること 2534 | - 型の内部を操作できるようにすること 2535 | - ユーザーが意図しない方法で内部関数の挙動を変えること 2536 | 2537 | 簡単にまとめると、きちんと意識して埋め込みましょうということになります。 2538 | 使うべきかチェックする簡単な方法は、「埋め込みたい型の公開されているメソッドやフィールドは全て埋め込まれる型に直接追加する必要があるか?」です。 2539 | 答えが、「いくつかはある」あるいは「ない」の場合は埋め込みは使わず、フィールドを使いましょう。 2540 | 2541 | 2542 | 2543 | 2544 | 2578 | 2617 | 2640 |
BadGood
2545 | 2546 | ```go 2547 | type A struct { 2548 | // 悪い例: 2549 | // A.Lock() と A.Unlock() が 2550 | // 使えますが、メリットはありません。 2551 | // さらに、A の内部を操作できる 2552 | // ようになってしまいます。 2553 | sync.Mutex 2554 | } 2555 | ``` 2556 | 2557 | 2558 | 2559 | ```go 2560 | type countingWriteCloser struct { 2561 | // 良い例: 2562 | // Write() は特定の目的のために 2563 | // 外側のレイヤーに提供されます。 2564 | // そして、内部の型の Write() メソッドに 2565 | // 役割を移譲しています。 2566 | io.WriteCloser 2567 | 2568 | count int 2569 | } 2570 | 2571 | func (w *countingWriteCloser) Write(bs []byte) (int, error) { 2572 | w.count += len(bs) 2573 | return w.WriteCloser.Write(bs) 2574 | } 2575 | ``` 2576 | 2577 |
2579 | 2580 | ```go 2581 | type Book struct { 2582 | // 悪い例: 2583 | // ポインタなのでゼロ値の便利さを消してしまう 2584 | io.ReadWriter 2585 | 2586 | // other fields 2587 | } 2588 | 2589 | // later 2590 | 2591 | var b Book 2592 | b.Read(...) // panic: nil pointer 2593 | b.String() // panic: nil pointer 2594 | b.Write(...) // panic: nil pointer 2595 | ``` 2596 | 2597 | 2598 | 2599 | ```go 2600 | type Book struct { 2601 | // 良い例: 2602 | // 便利なゼロ値を持っている 2603 | bytes.Buffer 2604 | 2605 | // other fields 2606 | } 2607 | 2608 | // later 2609 | 2610 | var b Book 2611 | b.Read(...) // ok 2612 | b.String() // ok 2613 | b.Write(...) // ok 2614 | ``` 2615 | 2616 |
2618 | 2619 | ```go 2620 | type Client struct { 2621 | sync.Mutex 2622 | sync.WaitGroup 2623 | bytes.Buffer 2624 | url.URL 2625 | } 2626 | ``` 2627 | 2628 | 2629 | 2630 | ```go 2631 | type Client struct { 2632 | mtx sync.Mutex 2633 | wg sync.WaitGroup 2634 | buf bytes.Buffer 2635 | url url.URL 2636 | } 2637 | ``` 2638 | 2639 |
2641 | 2642 | ## Local Variable Declarations 2643 | 変数が明示的に設定される場合、`:=` 演算子を利用しましょう。 2644 | 2645 | 2646 | 2647 | 2660 |
BadGood
2648 | 2649 | ```go 2650 | var s = "foo" 2651 | ``` 2652 | 2653 | 2654 | 2655 | ```go 2656 | s := "foo" 2657 | ``` 2658 | 2659 |
2661 | 2662 | しかし空のスライスを宣言する場合は`var`キーワードを利用したほうがよいでしょう。[参考資料: Declearing Empty Slices]( https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices ) 2663 | 2664 | 2665 | 2666 | 2667 | 2694 |
BadGood
2668 | 2669 | ```go 2670 | func f(list []int) { 2671 | filtered := []int{} 2672 | for _, v := range list { 2673 | if v > 10 { 2674 | filtered = append(filtered, v) 2675 | } 2676 | } 2677 | } 2678 | ``` 2679 | 2680 | 2681 | 2682 | ```go 2683 | func f(list []int) { 2684 | var filtered []int 2685 | for _, v := range list { 2686 | if v > 10 { 2687 | filtered = append(filtered, v) 2688 | } 2689 | } 2690 | } 2691 | ``` 2692 | 2693 |
2695 | 2696 | ## nil is a valid slice 2697 | `nil` は長さ0のスライスとして有効です。 2698 | つまり以下のものが有効です。 2699 | 2700 | * 長さ0のスライスを返す代わりに`nil`を返す 2701 | 2702 | 2703 | 2704 | 2721 |
BadGood
2705 | 2706 | ```go 2707 | if x == "" { 2708 | return []int{} 2709 | } 2710 | ``` 2711 | 2712 | 2713 | 2714 | ```go 2715 | if x == "" { 2716 | return nil 2717 | } 2718 | ``` 2719 | 2720 |
2722 | 2723 | * スライスが空かチェックするためには`nil`かチェックするのではなく`len(s) == 0`でチェックする 2724 | 2725 | 2726 | 2727 | 2744 |
BadGood
2728 | 2729 | ```go 2730 | func isEmpty(s []string) bool { 2731 | return s == nil 2732 | } 2733 | ``` 2734 | 2735 | 2736 | 2737 | ```go 2738 | func isEmpty(s []string) bool { 2739 | return len(s) == 0 2740 | } 2741 | ``` 2742 | 2743 |
2745 | 2746 | * varで宣言しただけのゼロ値が有効 2747 | 2748 | 2749 | 2750 | 2780 |
BadGood
2751 | 2752 | ```go 2753 | nums := []int{} 2754 | // or, nums := make([]int) 2755 | 2756 | if add1 { 2757 | nums = append(nums, 1) 2758 | } 2759 | 2760 | if add2 { 2761 | nums = append(nums, 2) 2762 | } 2763 | ``` 2764 | 2765 | 2766 | 2767 | ```go 2768 | var nums []int 2769 | 2770 | if add1 { 2771 | nums = append(nums, 1) 2772 | } 2773 | 2774 | if add2 { 2775 | nums = append(nums, 2) 2776 | } 2777 | ``` 2778 | 2779 |
2781 | 2782 | ## Reduce Scope of Variables 2783 | できる限り変数のスコープを減らしましょう。ただネストを浅くすることとバランスを考えてください。 2784 | 2785 | 2786 | 2787 | 2788 | 2806 |
BadGood
2789 | 2790 | ```go 2791 | err := ioutil.WriteFile(name, data, 0644) 2792 | if err != nil { 2793 | return err 2794 | } 2795 | ``` 2796 | 2797 | 2798 | 2799 | ```go 2800 | if err := ioutil.WriteFile(name, data, 0644); err != nil { 2801 | return err 2802 | } 2803 | ``` 2804 | 2805 |
2807 | 2808 | もし関数の戻り値をifの外で利用する場合、あまりスコープを縮めようとしなくてもよいでしょう。 2809 | 2810 | 2811 | 2812 | 2813 | 2846 |
BadGood
2814 | 2815 | ```go 2816 | if data, err := ioutil.ReadFile(name); err == nil { 2817 | err = cfg.Decode(data) 2818 | if err != nil { 2819 | return err 2820 | } 2821 | 2822 | fmt.Println(cfg) 2823 | return nil 2824 | } else { 2825 | return err 2826 | } 2827 | ``` 2828 | 2829 | 2830 | 2831 | ```go 2832 | data, err := ioutil.ReadFile(name) 2833 | if err != nil { 2834 | return err 2835 | } 2836 | 2837 | if err := cfg.Decode(data); err != nil { 2838 | return err 2839 | } 2840 | 2841 | fmt.Println(cfg) 2842 | return nil 2843 | ``` 2844 | 2845 |
2847 | 2848 | ## Avoid Naked Parameters 2849 | 値をそのまま関数の引数に入れることは可読性を損ないます。 2850 | もし分かりづらいならC言語スタイルのコメントで読みやすくしましょう。 2851 | 2852 | 2853 | 2854 | 2855 | 2872 |
BadGood
2856 | 2857 | ```go 2858 | // func printInfo(name string, isLocal, done bool) 2859 | 2860 | printInfo("foo", true, true) 2861 | ``` 2862 | 2863 | 2864 | 2865 | ```go 2866 | // func printInfo(name string, isLocal, done bool) 2867 | 2868 | printInfo("foo", true /* isLocal */, true /* done */) 2869 | ``` 2870 | 2871 |
2873 | 2874 | よりよいのはただの`bool`を自作の型で置き換えることです。こうすると型安全ですし可読性も上がります。 2875 | 更に将来的にtrue/false以外の状態も利用可能に修正することもできます。 2876 | 2877 | ```go 2878 | type Region int 2879 | 2880 | const ( 2881 | UnknownRegion Region = iota 2882 | Local 2883 | ) 2884 | 2885 | type Status int 2886 | 2887 | const ( 2888 | StatusReady = iota + 1 2889 | StatusDone 2890 | // Maybe we will have a StatusInProgress in the future. 2891 | ) 2892 | 2893 | func printInfo(name string, region Region, status Status) 2894 | ``` 2895 | 2896 | ## Use Raw String Literals to Avoid Escaping 2897 | Goは複数行や引用符のために[`Raw string literal`]( https://golang.org/ref/spec#raw_string_lit )をサポートしています。 2898 | これらをうまく使って手動でエスケープした読みづらい文字列を避けてください。 2899 | 2900 | 2901 | 2902 | 2903 | 2916 |
BadGood
2904 | 2905 | ```go 2906 | wantError := "unknown name:\"test\"" 2907 | ``` 2908 | 2909 | 2910 | 2911 | ```go 2912 | wantError := `unknown error:"test"` 2913 | ``` 2914 | 2915 |
2917 | 2918 | ## Initializing Structs 2919 | 2920 | ### Use Field Names to Initialize Structs 2921 | 2922 | 構造体を初期化する際にはフィールド名を書くようにしましょう。 2923 | [`go vet`]( https://golang.org/cmd/vet/ )でこのルールは指摘されます。 2924 | 2925 | 2926 | 2927 | 2928 | 2945 |
BadGood
2929 | 2930 | ```go 2931 | k := User{"John", "Doe", true} 2932 | ``` 2933 | 2934 | 2935 | 2936 | ```go 2937 | k := User{ 2938 | FirstName: "John", 2939 | LastName: "Doe", 2940 | Admin: true, 2941 | } 2942 | ``` 2943 | 2944 |
2946 | 2947 | 例外としてフィールド数が3以下のテストケースなら省略してもよいです。 2948 | 2949 | ```go 2950 | tests := []struct{ 2951 | op Operation 2952 | want string 2953 | }{ 2954 | {Add, "add"}, 2955 | {Subtract, "subtract"}, 2956 | } 2957 | ``` 2958 | 2959 | ### Omit Zero Value Fields in Structs 2960 | フィールド名を使って構造体を初期化するときは、意味のあるコンテキストを提供しない場合はフィールド名を省略しましょう。Goが自動的に型に応じたゼロ値を設定してくれます 2961 | 2962 | 2963 | 2964 | 2965 | 2986 |
BadGood
2966 | 2967 | ```go 2968 | user := User{ 2969 | FirstName: "John", 2970 | LastName: "Doe", 2971 | MiddleName: "", 2972 | Admin: false, 2973 | } 2974 | ``` 2975 | 2976 | 2977 | 2978 | ```go 2979 | user := User{ 2980 | FirstName: "John", 2981 | LastName: "Doe", 2982 | } 2983 | ``` 2984 | 2985 |
2987 | 2988 | 省略されたフィールドはデフォルトのゼロ値持つことは読み手の負荷を下げることができます。デフォルト値ではない値だけが指定されているからです。 2989 | 2990 | ゼロ値をあえてセットする意味がある場合もあります。例えば、[Test Tables]( #test-tables ) で指定するテストケースではゼロ値でも設定することは役に立ちます。 2991 | 2992 | ### Use `var` for Zero Value Structs 2993 | 2994 | 全てのフィールドを省略して宣言するときは、 `var` を使って構造体の宣言をしましょう。 2995 | 2996 | 2997 | 2998 | 2999 | 3000 | 3013 |
BadGood
3001 | 3002 | ```go 3003 | user := User{} 3004 | ``` 3005 | 3006 | 3007 | 3008 | ```go 3009 | var user User 3010 | ``` 3011 | 3012 |
3014 | 3015 | [map initialization]( #initializing-maps )でも似たようなことをしていますが、この方法を使うと、ゼロ値だけの構造体の宣言と、フィールドに値を指定する宣言を区別することができます。そしてこの方法は[declare empty slices]( #declaring-empty-slices )と同じ理由でおすすめの方法です。 3016 | 3017 | ### Initializing Struct References 3018 | 構造体の初期化と同じように構造体のポインタを初期化するときは`new(T)`ではなく、`&T{}`を使いましょう。 3019 | 3020 | 3021 | 3022 | 3023 | 3042 |
BadGood
3024 | 3025 | ```go 3026 | sval := T{Name: "foo"} 3027 | 3028 | // inconsistent 3029 | sptr := new(T) 3030 | sptr.Name = "bar" 3031 | ``` 3032 | 3033 | 3034 | 3035 | ```go 3036 | sval := T{Name: "foo"} 3037 | 3038 | sptr := &T{Name: "bar"} 3039 | ``` 3040 | 3041 |
3043 | 3044 | ## Initializing Maps 3045 | 空のマップを作る場合は `make(...)` を使い、コード内で実際にデータを入れます。 3046 | こうすることで、変数宣言と視覚的に区別でき、後でサイズヒントをつけやすくなります。 3047 | 3048 | 3049 | 3050 | 3051 | 3074 | 3083 |
BadGood
3052 | 3053 | ```go 3054 | var ( 3055 | // m1 is safe to read and write; 3056 | // m2 will panic on writes. 3057 | m1 = map[T1]T2{} 3058 | m2 map[T1]T2 3059 | ) 3060 | ``` 3061 | 3062 | 3063 | 3064 | ```go 3065 | var ( 3066 | // m1 is safe to read and write; 3067 | // m2 will panic on writes. 3068 | m1 = make(map[T1]T2) 3069 | m2 map[T1]T2 3070 | ) 3071 | ``` 3072 | 3073 |
3075 | 3076 | 変数宣言と初期化が視覚的に似ている 3077 | 3078 | 3079 | 3080 | 変数宣言と初期化が視覚的に区別しやすい 3081 | 3082 |
3084 | 3085 | 可能なら `make()` でマップを初期化する際にキャパシティのヒントを渡しましょう。 3086 | 詳細は[Prefer Specifying Map Capacity Hints]( #prefer-specifying-map-capacity-hints )を参照してください。 3087 | 3088 | 一方で、マップが予め決まった要素だけを保つ場合にはリテラルを使って初期化するほうがよいでしょう。 3089 | 3090 | 3091 | 3092 | 3093 | 3113 |
BadGood
3094 | 3095 | ```go 3096 | m := make(map[T1]T2, 3) 3097 | m[k1] = v1 3098 | m[k2] = v2 3099 | m[k3] = v3 3100 | ``` 3101 | 3102 | 3103 | 3104 | ```go 3105 | m := map[T1]T2{ 3106 | k1: v1, 3107 | k2: v2, 3108 | k3: v3, 3109 | } 3110 | ``` 3111 | 3112 |
3114 | 3115 | 大まかな原則は初期化時に決まった要素を追加するならマップリテラルを使い、それ以外なら `make()` (とあるならキャパシティのヒント)を使いましょう。 3116 | 3117 | ## Format Strings outside Printf 3118 | フォーマット用の文字列を`Printf`スタイルの外で定義する場合は`const`を使いましょう。 3119 | こうすることで`go vet`などの静的解析ツールでチェックしやすくなります。 3120 | 3121 | 3122 | 3123 | 3124 | 3139 |
BadGood
3125 | 3126 | ```go 3127 | msg := "unexpected values %v, %v\n" 3128 | fmt.Printf(msg, 1, 2) 3129 | ``` 3130 | 3131 | 3132 | 3133 | ```go 3134 | const msg = "unexpected values %v, %v\n" 3135 | fmt.Printf(msg, 1, 2) 3136 | ``` 3137 | 3138 |
3140 | 3141 | ## Naming Printf-style Functions 3142 | `Printf`スタイルの関数を使う場合、`go vet`がフォーマットをチェックできるか確認しましょう。 3143 | 3144 | これは可能であれば`Printf`スタイルの関数名を使う必要があることを示しています。 3145 | `go vet`はデフォルトでこれらの関数をチェックします。 3146 | 3147 | 事前に定義された関数名を使わない場合、関数名の最後を`f`にしましょう。 3148 | 例えば`Wrap`ではなく`Wrapf`にします。 3149 | `go vet`は特定の`Printf`スタイルのチェックができるようになっていますが、末尾が`f`である必要があります。 3150 | 3151 | ```shell 3152 | $ go vet -printfuncs=wrapf,statusf 3153 | ``` 3154 | 3155 | [go vet: Printf family check]( https://kuzminva.wordpress.com/2017/11/07/go-vet-printf-family-check/ )を更に参照してください。 3156 | 3157 | # Patterns 3158 | ## Test Tables 3159 | [サブテスト]( https://blog.golang.org/subtests )を利用したテーブルドリブンテストでコアのテストロジックを繰り返すときにコードの重複を避けるようにしましょう。 3160 | 3161 | 3162 | 3163 | 3164 | 3233 |
BadGood
3165 | 3166 | ```go 3167 | // func TestSplitHostPort(t *testing.T) 3168 | 3169 | host, port, err := net.SplitHostPort("192.0.2.0:8000") 3170 | require.NoError(t, err) 3171 | assert.Equal(t, "192.0.2.0", host) 3172 | assert.Equal(t, "8000", port) 3173 | 3174 | host, port, err = net.SplitHostPort("192.0.2.0:http") 3175 | require.NoError(t, err) 3176 | assert.Equal(t, "192.0.2.0", host) 3177 | assert.Equal(t, "http", port) 3178 | 3179 | host, port, err = net.SplitHostPort(":8000") 3180 | require.NoError(t, err) 3181 | assert.Equal(t, "", host) 3182 | assert.Equal(t, "8000", port) 3183 | 3184 | host, port, err = net.SplitHostPort("1:8") 3185 | require.NoError(t, err) 3186 | assert.Equal(t, "1", host) 3187 | assert.Equal(t, "8", port) 3188 | ``` 3189 | 3190 | 3191 | 3192 | ```go 3193 | // func TestSplitHostPort(t *testing.T) 3194 | 3195 | tests := []struct{ 3196 | give string 3197 | wantHost string 3198 | wantPort string 3199 | }{ 3200 | { 3201 | give: "192.0.2.0:8000", 3202 | wantHost: "192.0.2.0", 3203 | wantPort: "8000", 3204 | }, 3205 | { 3206 | give: "192.0.2.0:http", 3207 | wantHost: "192.0.2.0", 3208 | wantPort: "http", 3209 | }, 3210 | { 3211 | give: ":8000", 3212 | wantHost: "", 3213 | wantPort: "8000", 3214 | }, 3215 | { 3216 | give: "1:8", 3217 | wantHost: "1", 3218 | wantPort: "8", 3219 | }, 3220 | } 3221 | 3222 | for _, tt := range tests { 3223 | t.Run(tt.give, func(t *testing.T) { 3224 | host, port, err := net.SplitHostPort(tt.give) 3225 | require.NoError(t, err) 3226 | assert.Equal(t, tt.wantHost, host) 3227 | assert.Equal(t, tt.wantPort, port) 3228 | }) 3229 | } 3230 | ``` 3231 | 3232 |
3234 | 3235 | テストテーブルを使うと、エラーメッセージへの情報の追加やテストケースの追加も簡単ですし、コードも少なくなります。 3236 | 3237 | テストケースのルールはテストケースの構造体のスライス名が `tests` 、ループ内のそれぞれのテストケースの変数名が `tt` とします。 3238 | 更に入力値と出力値をわかりやすくするために`give`や`want`などのプレフィックスをつけることを推奨しています。 3239 | 3240 | ```go 3241 | tests := []struct{ 3242 | give string 3243 | wantHost string 3244 | wantPort string 3245 | }{ 3246 | // ... 3247 | } 3248 | 3249 | for _, tt := range tests { 3250 | // ... 3251 | } 3252 | ``` 3253 | 3254 | ## Avoid Unnecessary Complexity in Table Tests 3255 | テーブルテストで実施するテストの中で条件付きのアサーションや分岐ロジックがあると、可読性が低くなり保守がとても難しくなります。 3256 | テーブルテストでは `for` の中で、複雑なコードや条件分岐を入れるべきではありません。 3257 | 3258 | テストが失敗してデバッグする必要があるとき、大きくて複雑なテーブルテストは可読性と保守性を大きく損ないます。 3259 | 3260 | そのようなテーブルテストはいくつかのテーブルテストに分割するか、そもそも別のテスト関数に分けるのも良いでしょう。 3261 | 3262 | いくつかの方針を紹介します。 3263 | 3264 | - 振る舞いが小さくなるように意識する 3265 | - 条件付きのアサーションを避けて、テストの深さを最小にする 3266 | - テーブルのフィールドが全てのテストで使われているか確認する 3267 | - 全てのロジックが全てのテストケースで実行されるか確認する 3268 | 3269 | ここでいう「テストの深さ」とは、「そのテストで、前のアサーションを保持する必要がある連続したアサーションの数」といえます。 3270 | 循環複雑度に近いです。より薄いテストはよりアサーション間の関係が薄く、より重要な点はそれらのアサーションは条件付きになる可能性が低くなることです。 3271 | 3272 | 具体的に言うと、次のような状況だとテストを読むのが難しくなります。 3273 | 3274 | - テーブルのフィールドによって条件分岐が複数ある。( `shouldError` や `expectCall` などのフィールドがあると注意です ) 3275 | - 特定のモックの期待値のためにたくさんの `if` がある。( `shouldCallFoo` などのフィールドがあると怪しいです ) 3276 | - テーブルの中に関数がある。(フィールドの中に `setupMocks func(*FooMock)` がある ) 3277 | 3278 | しかし、変更された入力に基づいてのみ変化する動作をテストする場合、比較可能なユニットを別々のテストに分割して比較しにくくするのではなく、すべての入力に対してどのように動作が変化するかをよりよく説明するために、同様のケースをまとめてテーブルテストとすることが望ましい場合があります。 3279 | 3280 | テスト本体が短くてわかりやすければ、成功ケースと失敗ケースの分岐経路を1つにして、`shouldErr` のようなフィールドでエラーを期待することもできます。 3281 | 3282 | 3283 | 3284 | 3285 | 3365 |
BadGood
3286 | 3287 | ```go 3288 | func TestComplicatedTable(t *testing.T) { 3289 | tests := []struct { 3290 | give string 3291 | want string 3292 | wantErr error 3293 | shouldCallX bool 3294 | shouldCallY bool 3295 | giveXResponse string 3296 | giveXErr error 3297 | giveYResponse string 3298 | giveYErr error 3299 | }{ 3300 | // ... 3301 | } 3302 | 3303 | for _, tt := range tests { 3304 | t.Run(tt.give, func(t *testing.T) { 3305 | // setup mocks 3306 | ctrl := gomock.NewController(t) 3307 | xMock := xmock.NewMockX(ctrl) 3308 | if tt.shouldCallX { 3309 | xMock.EXPECT().Call().Return( 3310 | tt.giveXResponse, tt.giveXErr, 3311 | ) 3312 | } 3313 | yMock := ymock.NewMockY(ctrl) 3314 | if tt.shouldCallY { 3315 | yMock.EXPECT().Call().Return( 3316 | tt.giveYResponse, tt.giveYErr, 3317 | ) 3318 | } 3319 | 3320 | got, err := DoComplexThing(tt.give, xMock, yMock) 3321 | 3322 | // verify results 3323 | if tt.wantErr != nil { 3324 | require.EqualError(t, err, tt.wantErr) 3325 | return 3326 | } 3327 | require.NoError(t, err) 3328 | assert.Equal(t, want, got) 3329 | }) 3330 | } 3331 | } 3332 | ``` 3333 | 3334 | 3335 | 3336 | ```go 3337 | func TestShouldCallX(t *testing.T) { 3338 | // setup mocks 3339 | ctrl := gomock.NewController(t) 3340 | xMock := xmock.NewMockX(ctrl) 3341 | xMock.EXPECT().Call().Return("XResponse", nil) 3342 | 3343 | yMock := ymock.NewMockY(ctrl) 3344 | 3345 | got, err := DoComplexThing("inputX", xMock, yMock) 3346 | 3347 | require.NoError(t, err) 3348 | assert.Equal(t, "want", got) 3349 | } 3350 | 3351 | func TestShouldCallYAndFail(t *testing.T) { 3352 | // setup mocks 3353 | ctrl := gomock.NewController(t) 3354 | xMock := xmock.NewMockX(ctrl) 3355 | 3356 | yMock := ymock.NewMockY(ctrl) 3357 | yMock.EXPECT().Call().Return("YResponse", nil) 3358 | 3359 | _, err := DoComplexThing("inputY", xMock, yMock) 3360 | assert.EqualError(t, err, "Y failed") 3361 | } 3362 | ``` 3363 | 3364 |
3366 | 3367 | このコードの複雑さは変更したり、理解したり、このテストが正しいのかを証明するのが困難になります。 3368 | 3369 | 厳密なガイドラインはありませんが、システムへの複数の入力がある場合は、可読性と保守性を常に考慮して、個別のテストかテーブルテストか決定しましょう。 3370 | 3371 | ### Parallel Tests 3372 | 3373 | 並列テストなどの特殊なループ(例えば、ループの一部としてゴルーチンを起動し参照を保持するもの)では、ループのスコープ内の変数が正しい値を保持しているか注意しましょう。 3374 | 3375 | ```go 3376 | tests := []struct{ 3377 | give string 3378 | // ... 3379 | }{ 3380 | // ... 3381 | } 3382 | 3383 | for _, tt := range tests { 3384 | tt := tt // for t.Parallel 3385 | t.Run(tt.give, func(t *testing.T) { 3386 | t.Parallel() 3387 | // ... 3388 | }) 3389 | } 3390 | ``` 3391 | 3392 | この例では、 `t.Parallel()` を下で読んでいるので、 `tt` という変数をループの中で再度宣言する必要があります。 3393 | もしやらないと、ほとんどのテストで `tt` 変数が期待しない値になったり、テスト中に値が変更されてしまいます。 3394 | 3395 | ## Functional Options 3396 | Functional Option パターンは不透明なOption型を使って内部の構造体に情報を渡すパターンです。 3397 | 可変長引数を受け取り、それらを順に内部のオプションに渡します。 3398 | 3399 | コンストラクタや、公開されたAPIで3つ以上の多くの引数が必要な場合、このパターンを使うと良いでしょう。 3400 | 3401 | 3402 | 3403 | 3404 | 3445 | 3472 |
BadGood
3405 | 3406 | ```go 3407 | // package db 3408 | 3409 | func Open( 3410 | addr string, 3411 | cache bool, 3412 | logger *zap.Logger 3413 | ) (*Connection, error) { 3414 | // ... 3415 | } 3416 | ``` 3417 | 3418 | 3419 | 3420 | ```go 3421 | // package db 3422 | 3423 | type Option interface { 3424 | // ... 3425 | } 3426 | 3427 | func WithCache(c bool) Option { 3428 | // ... 3429 | } 3430 | 3431 | func WithLogger(log *zap.Logger) Option { 3432 | // ... 3433 | } 3434 | 3435 | // Open creates a connection. 3436 | func Open( 3437 | addr string, 3438 | opts ...Option, 3439 | ) (*Connection, error) { 3440 | // ... 3441 | } 3442 | ``` 3443 | 3444 |
3446 | 3447 | キャッシュやロガーはデフォルトを使う場合でも常に指定する必要があります 3448 | 3449 | ```go 3450 | db.Open(addr, db.DefaultCache, zap.NewNop()) 3451 | db.Open(addr, db.DefaultCache, log) 3452 | db.Open(addr, false /* cache */, zap.NewNop()) 3453 | db.Open(addr, false /* cache */, log) 3454 | ``` 3455 | 3456 | 3457 | 3458 | オプションは必要なら提供されます 3459 | 3460 | ```go 3461 | db.Open(addr) 3462 | db.Open(addr, db.WithLogger(log)) 3463 | db.Open(addr, db.WithCache(false)) 3464 | db.Open( 3465 | addr, 3466 | db.WithCache(false), 3467 | db.WithLogger(log), 3468 | ) 3469 | ``` 3470 | 3471 |
3473 | 3474 | 私達が紹介する方法は非公開のメソッドを持った `Option` インタフェースを使う方法です。 3475 | 指定されたオプションは非公開の `options` 構造体に保持されます。 3476 | 3477 | ```go 3478 | type options struct { 3479 | cache bool 3480 | logger *zap.Logger 3481 | } 3482 | 3483 | type Option interface { 3484 | apply(*options) 3485 | } 3486 | 3487 | type cacheOption bool 3488 | 3489 | func (c cacheOption) apply(opts *options) { 3490 | opts.cache = bool(c) 3491 | } 3492 | 3493 | func WithCache(c bool) Option { 3494 | return cacheOption(c) 3495 | } 3496 | 3497 | type loggerOption struct { 3498 | Log *zap.Logger 3499 | } 3500 | 3501 | func (l loggerOption) apply(opts *options) { 3502 | opts.logger = l.Log 3503 | } 3504 | 3505 | func WithLogger(log *zap.Logger) Option { 3506 | return loggerOption{Log: log} 3507 | } 3508 | 3509 | // Open creates a connection. 3510 | func Open( 3511 | addr string, 3512 | opts ...Option, 3513 | ) (*Connection, error) { 3514 | options := options{ 3515 | cache: defaultCache, 3516 | logger: zap.NewNop(), 3517 | } 3518 | 3519 | for _, o := range opts { 3520 | o.apply(&options) 3521 | } 3522 | 3523 | // ... 3524 | } 3525 | ``` 3526 | 3527 | このパターンを実装するためにクロージャを使っていますが、この方法は開発者により柔軟性をもたせ、デバッグやテストをしやすくなると考えています。 3528 | 特に、テストやモックなどで比較する際にクロージャを使うことで比較しやすくなります。 3529 | 更に、`options` に他のインタフェースを実装させる事もできます。 3530 | `fmt.Stringer` インタフェースを実装すると、設定を人間にわかりやすく表示させることも可能です。 3531 | 3532 | 更に以下の資料が参考になります。 3533 | 3534 | * [Self-referential functions and the design of options]( https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html ) 3535 | * [Functional options for friendly APIs]( https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis ) 3536 | 3537 | 3538 | # Linting 3539 | どんなおすすめの linter のセットよりも重要なのは、コードベース全体で一貫した linter を使うことです。 3540 | 3541 | 私達は最小限の linter として以下のものをおすすめしています。これだけあれば、一般的な問題を発見でき、不必要に厳しすぎることもなく、良い品質を確立することができるからです。 3542 | 3543 | - [errcheck]( https://github.com/kisielk/errcheck ) はエラーが正しく処理されているかを担保します 3544 | - [goimports]( https://godoc.org/golang.org/x/tools/cmd/goimports ) はライブラリのインポートの管理もするフォーマッタです 3545 | - [golint]( https://github.com/golang/lint ) は一般的なスタイルのミスを見つけます 3546 | - [govet]( https://golang.org/cmd/vet/ ) は一般的なミスを見つけます 3547 | - [staticcheck]( https://staticcheck.io/ ) は多くの静的チェックを行います 3548 | 3549 | ## Lint Runners 3550 | 3551 | 私達は、大きなコードベースでのパフォーマンスと、多くの linter を一度に設定できる点から、[golangci-lint]( https://github.com/golangci/golangci-lint ) をおすすめしています。 3552 | このガイドのリポジトリにはおすすめの [.golangci.yaml]( https://github.com/uber-go/guide/blob/master/.golangci.yml ) 設定ファイルがあります。 3553 | 3554 | golangci-lint は[多くのlinter]( https://golangci-lint.run/usage/linters/ )が使えます。前述した linter は基本のセットですが、チームが必要に応じて linter を追加することを推奨しています。 3555 | 3556 | --------------------------------------------------------------------------------