├── .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 | Bad | Good |
49 |
50 |
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 | |
85 |
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 | Bad | Good |
175 |
176 |
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 | |
191 |
192 |
193 | もし構造体のポインタを使う場合、mutexはポインタでないフィールドにする必要があります。
194 | 外部に公開されてない構造体なら、mutexを埋め込みで使うこともできます。
195 |
196 |
197 |
198 |
199 |
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 | |
246 |
247 |
248 |
249 | インターナルな型やmutexのインタフェースを実装している必要がある場合には埋め込みを使う |
250 | 公開されている型にはプライベートなフィールドを使う |
251 |
252 |
253 |
254 |
255 | ## Copy Slices and Maps at Boundaries
256 | スライスやマップは内部でデータへのポインタが含まれています。なのでコピーする際には注意してください。
257 |
258 | ### Receiving Slices and Maps
259 | 引数として受け取ってフィールドに保存したスライスは、他の箇所でデータが書き換わる可能性があることを覚えておいてください。
260 |
261 |
262 | Bad | Good |
263 |
264 |
265 |
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 | |
280 |
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 | |
296 |
297 |
298 |
299 |
300 |
301 | ### Returning Slices and Maps
302 | 同じように、公開せずに内部に保持しているスライスやマップが変更されることもあります。
303 |
304 |
305 | Bad | Good |
306 |
307 |
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 | |
352 |
353 |
354 | ## Defer to Clean Up
355 | ファイルや mutex のロックなどをクリーンアップするために defer を使おう
356 |
357 |
358 | Bad | Good |
359 |
360 |
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 | |
395 |
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 | Bad | Good |
410 |
411 |
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 | |
428 |
429 |
430 | ## Start Enums at One
431 | Go で enum を導入するときの標準的な方法は、型を定義して `const` のグループを作り、初期値を `iota` にすることです。
432 | 変数のデフォルト値はゼロ値です。なので通常はゼロ値ではない値から enum を始めるべきでしょう。
433 |
434 |
435 | Bad | Good |
436 |
437 |
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 | |
466 |
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 | Bad | Good |
504 |
505 |
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 | |
522 |
523 |
524 | ### Use `time.Duration` for periods of time
525 | 期間を扱うときには[time.Duration]( https://pkg.go.dev/time?tab=doc#Duration )型を使いましょう。
526 |
527 |
528 | Bad | Good |
529 |
530 |
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 | |
557 |
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 | Bad | Good |
582 |
583 |
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 | |
602 |
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 | No error matching | Error matching |
637 |
638 |
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 | |
678 |
679 |
680 | 動的文字列のエラーの場合、呼び出し側がエラー検知する必要がないならば [`fmt.Errorf`] を使い、検知する必要があるならば自前の`error`インターフェースを実装する型を使いましょう。
681 |
682 |
683 | No error matching | Error matching |
684 |
685 |
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 | |
733 |
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 | Bad | Good |
788 |
789 |
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 | |
822 |
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 | Description | Code |
898 |
899 |
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 | |
917 |
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 | |
933 |
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 | |
949 |
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 | |
972 |
973 |
974 | ## Handle Type Assertion Failures
975 | [型アサーション]( https://golang.org/ref/spec#Type_assertions )で1つの戻り値を受け取る場合、その型でなかったらパニックを起こします。
976 | 型アサーションではその型に変換できたかを示すbool値も同時に返ってくるので、それで事前にチェックしましょう。
977 |
978 |
979 | Bad | Good |
980 |
981 |
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 | |
997 |
998 |
999 | ## Don't Panic
1000 | プロダクションで動くコードはパニックを避けなければいけません。
1001 | パニックは連鎖的障害の主な原因です。
1002 | もしエラーが起きた場合、関数はエラーを返して、呼び出し元がどのようにエラーをハンドリングするか決めさせる必要があります。
1003 |
1004 |
1005 | Bad | Good |
1006 |
1007 |
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 | |
1049 |
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 | Bad | Good |
1064 |
1065 |
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 | |
1088 |
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 | Bad | Good |
1100 |
1101 |
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 | |
1142 |
1143 |
1144 | ## Avoid Mutable Globals
1145 | グローバル変数を変更するのは避けましょう。
1146 | 代わりに依存関係の注入を使って構造体に持たせるようにしましょう。
1147 | 関数ポインタを他の値と同じように構造体にもたせます。
1148 |
1149 |
1150 | Bad | Good |
1151 |
1152 |
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 | |
1186 |
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 | |
1218 |
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 | Bad | Good |
1243 |
1244 |
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 | |
1273 |
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 | Bad | Good |
1292 |
1293 |
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 | |
1329 |
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 | Bad | Good |
1351 |
1352 |
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 | |
1379 |
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 | |
1420 |
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 | Bad | Good |
1439 |
1440 |
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 | |
1475 |
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 | |
1521 |
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 | Bad | Good |
1536 |
1537 |
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 | |
1587 |
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 | Bad | Good |
1603 |
1604 |
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 | |
1668 |
1669 |
1670 | ## Use field tags in marshaled structs
1671 | JSON や YAML あるいは他のタグを使ったフィールド名をサポートするフォーマットに変換する場合、関連するタグを使ってアノテーションをつけましょう。
1672 |
1673 |
1674 |
1675 | Bad | Good |
1676 |
1677 |
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 | |
1707 |
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 | Bad | Good |
1732 |
1733 |
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 | |
1772 |
1773 |
1774 | ゴルーチンを止める方法は無い。プログラムが終了するまで残り続ける
1775 |
1776 | |
1777 |
1778 | ゴルーチンは `close(stop)` で停止できる。そして `<-done` で待つこともできる。
1779 |
1780 | |
1781 |
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 | Bad | Good |
1825 |
1826 |
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 | |
1872 |
1873 |
1874 | ユーザーがこのパッケージを公開すると、ゴルーチンを無条件に作成し、止める方法はありません。
1875 |
1876 | |
1877 |
1878 | ユーザーがリクエストすると、ワーカーを起動します。`Shutdown` メソッドで、ワーカーを停止し使っているリソースを開放できる手段も提供しています。
1879 |
1880 | [Wait for goroutines to exit]( #wait-for-gorutines-to-exit ) でも話したように、ワーカーが複数ゴルーチンを使う場合は `WaitGroup` を使いましょう。
1881 |
1882 | |
1883 |
1884 |
1885 |
1886 | # Performance
1887 | パフォーマンスガイドラインは特によく実行される箇所にのみ適用されます。
1888 |
1889 | ## Prefer strconv over fmt
1890 | 数字と文字列を単に変換する場合、`fmt` パッケージよりも `strconv` パッケージのほうが高速に実行されます。
1891 |
1892 |
1893 | Bad | Good |
1894 |
1895 |
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 | |
1912 |
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 | |
1925 |
1926 |
1927 | ## Avoid string-to-byte conversion
1928 | 固定の文字列からバイト列を何度も生成するのは避けましょう。
1929 | 代わりに変数に格納してそれを使うようにしましょう。
1930 |
1931 |
1932 | Bad | Good |
1933 |
1934 |
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 | |
1952 |
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 | |
1965 |
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 | Bad | Good |
1981 |
1982 |
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 | |
2003 |
2004 |
2005 | `m` は初期化時にサイズのヒントが与えられませんでした。
2006 | そのため余計にアロケーションが発生する可能性があります。
2007 |
2008 | |
2009 |
2010 | `m` にはサイズのヒントが与えられています。
2011 | 要素の追加時にアロケーションの回数を押さえられます。
2012 |
2013 | |
2014 |
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 | Bad | Good |
2044 |
2045 |
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 | |
2062 |
2063 |
2064 | これはパッケージ定数やパッケージ変数、型定義などにも利用できます。
2065 |
2066 |
2067 | Bad | Good |
2068 |
2069 |
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 | |
2107 |
2108 |
2109 | 関係が近いものだけをグループ化しましょう。
2110 | 関係ないものまでグループ化するのは避けましょう。
2111 |
2112 |
2113 | Bad | Good |
2114 |
2115 |
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 | |
2143 |
2144 |
2145 | グループ化を使う場所に制限は無いので、関数内でも使うことができます。
2146 |
2147 |
2148 | Bad | Good |
2149 |
2150 |
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 | |
2177 |
2178 |
2179 | ## Import Group Ordering
2180 | import のグループは次の2つに分けるべきです。
2181 |
2182 | 1. 標準パッケージ
2183 | 2. それ以外
2184 |
2185 | goimports がデフォルトで適用してくれます。
2186 |
2187 |
2188 | Bad | Good |
2189 |
2190 |
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 | |
2214 |
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 | Bad | Good |
2249 |
2250 |
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 | |
2275 |
2276 |
2277 | ## Function Grouping and Ordering
2278 |
2279 | * 関数は呼び出される順番におおまかにソートされるべきです
2280 | * 関数はレシーバーごとにまとめられているべきです。
2281 |
2282 | なので、`struct`、`const`、`var`の次にパッケージ外に公開されている関数が来るべきです。
2283 |
2284 | `newXYZ()`や`NewXYZ()`は型が定義されたあと、他のメソッドの前に定義されている必要があります。
2285 |
2286 | 関数はレシーバーごとにまとめられているので、ユーティリティな関数はファイルの最後の方に出てくるはずです。
2287 |
2288 |
2289 |
2290 | Bad | Good |
2291 |
2292 |
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 | |
2329 |
2330 |
2331 | ## Reduce Nesting
2332 | エラーや特殊ケースなどは早めにハンドリングして`return`したりループ内では`continue`や`break`してネストが浅いコードを目指しましょう。
2333 | ネストが深いコードを減らしていきましょう。
2334 |
2335 |
2336 | Bad | Good |
2337 |
2338 |
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 | |
2373 |
2374 |
2375 | ## Unnecessary Else
2376 | if-else のどちらでも変数に代入する場合、条件に一致した場合に上書きするようにしましょう。
2377 |
2378 |
2379 | Bad | Good |
2380 |
2381 |
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 | |
2402 |
2403 |
2404 | ## Top-level Variable Declarations
2405 | パッケージ変数で式と同じ型なら型名を指定しないようにしましょう。
2406 |
2407 |
2408 | Bad | Good |
2409 |
2410 |
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 | |
2429 |
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 | Bad | Good |
2451 |
2452 |
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 | |
2486 |
2487 |
2488 | ## Embedding in Structs
2489 |
2490 | 埋め込まれた型は構造体の定義の最初に置くべきです。
2491 | また、通常のフィールドと区別するために1行開ける必要があります。
2492 |
2493 |
2494 | Bad | Good |
2495 |
2496 |
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 | |
2516 |
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 | Bad | Good |
2543 |
2544 |
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 | |
2578 |
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 | |
2617 |
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 | |
2640 |
2641 |
2642 | ## Local Variable Declarations
2643 | 変数が明示的に設定される場合、`:=` 演算子を利用しましょう。
2644 |
2645 | Bad | Good |
2646 |
2647 |
2648 |
2649 | ```go
2650 | var s = "foo"
2651 | ```
2652 |
2653 | |
2654 |
2655 | ```go
2656 | s := "foo"
2657 | ```
2658 |
2659 | |
2660 |
2661 |
2662 | しかし空のスライスを宣言する場合は`var`キーワードを利用したほうがよいでしょう。[参考資料: Declearing Empty Slices]( https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices )
2663 |
2664 |
2665 | Bad | Good |
2666 |
2667 |
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 | |
2694 |
2695 |
2696 | ## nil is a valid slice
2697 | `nil` は長さ0のスライスとして有効です。
2698 | つまり以下のものが有効です。
2699 |
2700 | * 長さ0のスライスを返す代わりに`nil`を返す
2701 |
2702 | Bad | Good |
2703 |
2704 |
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 | |
2721 |
2722 |
2723 | * スライスが空かチェックするためには`nil`かチェックするのではなく`len(s) == 0`でチェックする
2724 |
2725 | Bad | Good |
2726 |
2727 |
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 | |
2744 |
2745 |
2746 | * varで宣言しただけのゼロ値が有効
2747 |
2748 | Bad | Good |
2749 |
2750 |
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 | |
2780 |
2781 |
2782 | ## Reduce Scope of Variables
2783 | できる限り変数のスコープを減らしましょう。ただネストを浅くすることとバランスを考えてください。
2784 |
2785 |
2786 | Bad | Good |
2787 |
2788 |
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 | |
2806 |
2807 |
2808 | もし関数の戻り値をifの外で利用する場合、あまりスコープを縮めようとしなくてもよいでしょう。
2809 |
2810 |
2811 | Bad | Good |
2812 |
2813 |
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 | |
2846 |
2847 |
2848 | ## Avoid Naked Parameters
2849 | 値をそのまま関数の引数に入れることは可読性を損ないます。
2850 | もし分かりづらいならC言語スタイルのコメントで読みやすくしましょう。
2851 |
2852 |
2853 | Bad | Good |
2854 |
2855 |
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 | |
2872 |
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 | Bad | Good |
2902 |
2903 |
2904 |
2905 | ```go
2906 | wantError := "unknown name:\"test\""
2907 | ```
2908 |
2909 | |
2910 |
2911 | ```go
2912 | wantError := `unknown error:"test"`
2913 | ```
2914 |
2915 | |
2916 |
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 | Bad | Good |
2927 |
2928 |
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 | |
2945 |
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 | Bad | Good |
2964 |
2965 |
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 | |
2986 |
2987 |
2988 | 省略されたフィールドはデフォルトのゼロ値持つことは読み手の負荷を下げることができます。デフォルト値ではない値だけが指定されているからです。
2989 |
2990 | ゼロ値をあえてセットする意味がある場合もあります。例えば、[Test Tables]( #test-tables ) で指定するテストケースではゼロ値でも設定することは役に立ちます。
2991 |
2992 | ### Use `var` for Zero Value Structs
2993 |
2994 | 全てのフィールドを省略して宣言するときは、 `var` を使って構造体の宣言をしましょう。
2995 |
2996 |
2997 |
2998 | Bad | Good |
2999 |
3000 |
3001 |
3002 | ```go
3003 | user := User{}
3004 | ```
3005 |
3006 | |
3007 |
3008 | ```go
3009 | var user User
3010 | ```
3011 |
3012 | |
3013 |
3014 |
3015 | [map initialization]( #initializing-maps )でも似たようなことをしていますが、この方法を使うと、ゼロ値だけの構造体の宣言と、フィールドに値を指定する宣言を区別することができます。そしてこの方法は[declare empty slices]( #declaring-empty-slices )と同じ理由でおすすめの方法です。
3016 |
3017 | ### Initializing Struct References
3018 | 構造体の初期化と同じように構造体のポインタを初期化するときは`new(T)`ではなく、`&T{}`を使いましょう。
3019 |
3020 |
3021 | Bad | Good |
3022 |
3023 |
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 | |
3042 |
3043 |
3044 | ## Initializing Maps
3045 | 空のマップを作る場合は `make(...)` を使い、コード内で実際にデータを入れます。
3046 | こうすることで、変数宣言と視覚的に区別でき、後でサイズヒントをつけやすくなります。
3047 |
3048 |
3049 | Bad | Good |
3050 |
3051 |
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 | |
3074 |
3075 |
3076 | 変数宣言と初期化が視覚的に似ている
3077 |
3078 | |
3079 |
3080 | 変数宣言と初期化が視覚的に区別しやすい
3081 |
3082 | |
3083 |
3084 |
3085 | 可能なら `make()` でマップを初期化する際にキャパシティのヒントを渡しましょう。
3086 | 詳細は[Prefer Specifying Map Capacity Hints]( #prefer-specifying-map-capacity-hints )を参照してください。
3087 |
3088 | 一方で、マップが予め決まった要素だけを保つ場合にはリテラルを使って初期化するほうがよいでしょう。
3089 |
3090 |
3091 | Bad | Good |
3092 |
3093 |
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 | |
3113 |
3114 |
3115 | 大まかな原則は初期化時に決まった要素を追加するならマップリテラルを使い、それ以外なら `make()` (とあるならキャパシティのヒント)を使いましょう。
3116 |
3117 | ## Format Strings outside Printf
3118 | フォーマット用の文字列を`Printf`スタイルの外で定義する場合は`const`を使いましょう。
3119 | こうすることで`go vet`などの静的解析ツールでチェックしやすくなります。
3120 |
3121 |
3122 | Bad | Good |
3123 |
3124 |
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 | |
3139 |
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 | Bad | Good |
3163 |
3164 |
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 | |
3233 |
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 | Bad | Good |
3284 |
3285 |
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 | |
3365 |
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 | Bad | Good |
3403 |
3404 |
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 | |
3445 |
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 | |
3472 |
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 |
--------------------------------------------------------------------------------