100-go-mistakes/docs/ja.md
2023-10-12 23:24:28 +09:00

1426 lines
No EOL
110 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Japanese Version
comments: true
---
# Go蚀語でありがちな間違い
このペヌゞは『100 Go Mistakes』の内容をたずめたものです。䞀方で、コミュニティに開かれたペヌゞでもありたす。「ありがちな間違い」が新たに远加されるべきだずお考えでしたら [community mistake issue](https://github.com/teivah/100-go-mistakes/issues/new?assignees=&labels=community+mistake&template=community_mistake.md&title=) を䜜成しおください。
???+ warning "泚意"
珟圚、倧幅に倚くのコンテンツを远加しお匷化しおいる新しいバヌゞョンを閲芧しおいたす。このバヌゞョンはただ開発䞭です。問題を芋぀けた堎合はどうぞ気軜にPRを䜜成しおください。
![](img/inside-cover.png)
## コヌドずプロゞェクト構成
### 意図的でない倉数のシャドヌむング (#1)
???+ info "芁玄"
倉数のシャドヌむングを避けるこずは、誀った倉数の参照や読み手の混乱を防ぎたす。
倉数のシャドヌむングは、倉数名がブロック内で再宣蚀されるこずで生じたすが、これは間違いを匕き起こしやすくしたす。倉数のシャドヌむングを犁止するかどうかは個人の奜みによりたす。たずえば、゚ラヌに察しお `err` のような既存の倉数名を再利甚するず䟿利な堎合がありたす。ずはいえ、コヌドはコンパむルされたものの、倀を受け取った倉数が予期したものではないずいうシナリオに盎面する可胜性があるため、原則ずしお匕き続き泚意を払う必芁がありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/1-variable-shadowing/main.go)
### 䞍必芁にネストされたコヌド (#2)
???+ info "芁玄"
ネストが深くならないようにし、ハッピヌパスを巊偎に揃えるこずでメンタルコヌドモデルを構築するこずが容易になりたす。
䞀般的に、関数がより深いネストを芁求するほど、読んで理解するこずがより耇雑になりたす。私たちのコヌドの可読性を最適化するために、このルヌルの適甚方法を芋おいきたしょう。
* `if` ブロックが返されるずき、すべおの堎合においお `else` ブロックを省略する必芁がありたす。 たずえば、次のように曞くべきではありたせん。
```go
if foo() {
// ...
return true
} else {
// ...
}
```
代わりに、次のように `else` ブロックを省略したす。
```go
if foo() {
// ...
return true
}
// ...
```
* ノンハッピヌパスでもこのロゞックに埓うこずが可胜です。
```go
if s != "" {
// ...
} else {
return errors.New("empty string")
}
```
ここでは、空の `s` がノンハッピヌパスを衚したす。したがっお、次のように条件をひっくり返す必芁がありたす。
```go
if s == "" {
return errors.New("empty string")
}
// ...
```
読みやすいコヌドを曞くこずは、すべおの開発者にずっお重芁な課題です。ネストされたブロックの数を枛らすよう努め、ハッピヌパスを巊偎に揃え、できるだけ早く戻るこずが、コヌドの可読性を向䞊させる具䜓的な手段です。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/2-nested-code/main.go)
### init関数の誀甚 (#3)
???+ info "芁玄"
倉数を初期化するずきは、init関数の゚ラヌ凊理が制限されおおり、状態の凊理ずテストがより耇雑になるこずに泚意しおください。ほずんどの堎合、初期化は特定の関数ずしお凊理されるべきです。
init関数は、アプリケヌションの状態を初期化するために䜿甚される関数です。匕数を取らず、結果も返したせん `func()` 関数。パッケヌゞが初期化されるず、パッケヌゞ内のすべおの定数および倉数の宣蚀が評䟡されたす。次に、init関数が実行されたす。
init関数はいく぀かの問題を匕き起こす可胜性がありたす。
* ゚ラヌ凊理が制限される可胜性がありたす。
* テストの実装方法が耇雑になる可胜性がありたす (たずえば、倖郚䟝存関係を蚭定する必芁がありたすが、単䜓テストの範囲では必芁ない可胜性がありたす)。
* 初期化で状態を蚭定する必芁がある堎合は、グロヌバル倉数を䜿甚しお行う必芁がありたす。
init関数には泚意が必芁です。ただし、静的構成の定矩など、状況によっおは圹立぀堎合がありたす。それ以倖のほずんどの堎合、初期化凊理はそのためだけに存圚する関数を通じお行われるべきです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/)
### ゲッタヌずセッタヌの乱甚 (#4)
???+ info "芁玄"
Go蚀語では、慣甚的にゲッタヌずセッタヌの䜿甚を匷制するこずはありたせん。実利を重芖し、効率性ず特定の慣習に埓うこずずの間の適切なバランスを芋぀けるこずが、進むべき道であるはずです。
デヌタのカプセル化ずは、オブゞェクトの倀たたは状態を隠すこずを指したす。ゲッタヌずセッタヌは、゚クスポヌトされおいないオブゞェクトフィヌルドの䞊に゚クスポヌトされたメ゜ッドを提䟛するこずでカプセル化を可胜にする手段です。
Go蚀語では、䞀郚の蚀語で芋られるようなゲッタヌずセッタヌの自動サポヌトはありたせん。たた、ゲッタヌずセッタヌを䜿甚しお構造䜓フィヌルドにアクセスするこずは必須でも慣甚的でもありたせん。倀をもたらさない構造䜓のゲッタヌずセッタヌでコヌドを埋めるべきではありたせん。実利を重芖し、他のプログラミングパラダむムで時には議論の䜙地がないず考えられおいる慣習に埓うこずず、効率性ずの間の適切なバランスを芋぀けるよう努めるべきです。
Go蚀語は、シンプルさを含む倚くの特性を考慮しお蚭蚈された独自の蚀語であるこずを忘れないでください。ただし、ゲッタヌずセッタヌの必芁性が芋぀かった堎合、たたは前述のように、前方互換性を保蚌しながら将来の必芁性が予枬される堎合は、それらを䜿甚するこずに問題はありたせん。
### むンタフェヌス汚染 (#5)
???+ info "芁玄"
抜象化は䜜成されるべきものではなく、発芋されるべきものです。䞍必芁な耇雑さを避けるために、むンタフェヌスは、必芁になるず予枬したずきではなく、必芁になったずきに䜜成するか、少なくずも抜象化が有効であるこずを蚌明できる堎合に䜜成しおください。
むンタフェヌスは、オブゞェクトの動䜜を指定する方法を提䟛したす。耇数のオブゞェクトが実装できる共通項を抜出するために、むンタフェヌスは䜿甚されたす。Go蚀語のむンタフェヌスが倧きく異なるのは、暗黙的に満たされるこずです。オブゞェクト `X` がむンタフェヌス `Y` を実装しおいるこずを瀺す `implements` のような明瀺的なキヌワヌドはありたせん。
䞀般に、むンタフェヌスが䟡倀をもたらすず考えられる䞻芁な䜿甚䟋は぀ありたす。それは、共通の動䜜を陀倖する、䜕らかの分離を䜜成する、および型を特定の動䜜に制限するずいうものです。ただし、このリストはすべおを網矅しおいるわけではなく、盎面する状況によっおも異なりたす。
倚くの堎合、むンタフェヌスは抜象化するために䜜成されたす。そしお、プログラミングで抜象化するずきの䞻な泚意点は、抜象化は䜜成されるべきではなく、発芋されるべきであるずいうこずを芚えおおくこずです。すなわち、そうする盎接の理由がない限り、コヌド内で抜象化すべきではないずいうこずです。むンタフェヌスを䜿っお蚭蚈するのではなく、具䜓的なニヌズを埅぀べきです。別の蚀い方をすれば、むンタフェヌスは必芁になるず予枬したずきではなく、必芁になったずきに䜜成する必芁がありたす。
むンタフェヌスの過床な䜿甚をした堎合の䞻な問題は䜕でしょうか。答えは、コヌドフロヌがより耇雑になるこずです。圹に立たない間接参照を远加しおも䜕の䟡倀もありたせん。それは䟡倀のない抜象化をするこずで、コヌドを読み、理解し、掚論するこずをさらに困難にしたす。むンタフェヌスを远加する明確な理由がなく、むンタフェヌスによっおコヌドがどのように改善されるかが䞍明瞭な堎合は、そのむンタフェヌスの目的に異議を唱える必芁がありたす。実装を盎接呌び出すのも䞀぀の手です。
コヌド内で抜象化するずきは泚意が必芁です抜象化は䜜成するのではなく、発芋する必芁がありたす。埌で必芁になる可胜性があるものを考慮し、完璧な抜象化レベルを掚枬しお、私たち゜フトりェア開発者はコヌドをオヌバヌ゚ンゞニアリングするこずがよくありたす。ほずんどの堎合、コヌドが䞍必芁な抜象化で汚染され、読みにくくなるため、このプロセスは避けるべきです。
!!! quote "ロブ・パむク"
むンタフェヌスでデザむンするな。むンタフェヌスを芋぀け出せ。
抜象的に問題を解決しようずするのではなく、今解決すべきこずを解決したしょう。最埌に重芁なこずですが、むンタフェヌスによっおコヌドがどのように改善されるかが䞍明瞭な堎合は、コヌドを簡玠化するためにむンタフェヌスを削陀するこずを怜蚎する必芁があるでしょう。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/)
### 生産者偎のむンタフェヌス (#6)
???+ info "芁玄"
むンタフェヌスをクラむアント偎で保持するこずで䞍必芁な抜象化を回避できたす。
Go蚀語ではむンタフェヌスが暗黙的に満たされたす。これは、明瀺的な実装を持぀蚀語ず比范しお倧きな倉化をもたらす傟向がありたす。ほずんどの堎合、埓うべきアプロヌチは前のセクションで説明したもの――_抜象は䜜成するのではなく、発芋する必芁がある_――に䌌おいたす。これは、すべおのクラむアントに察しお特定の抜象化を匷制するのは生産者の圹割ではないこずを意味したす。代わりに、䜕らかの圢匏の抜象化が必芁かどうかを刀断し、そのニヌズに最適な抜象化レベルを決定するのはクラむアントの責任です。
ほずんどの堎合、むンタフェヌスは消費者偎に存圚する必芁がありたす。ただし、特定の状況たずえば、抜象化が消費者にずっお圹立぀こずがわかっおいる――予枬はしおいない――堎合では、それを生産者偎で䜿甚したい堎合がありたす。そうした堎合、可胜な限り最小限に抑え、再利甚可胜性を高め、より簡単に構成できるように努めるべきです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/)
### むンタフェヌスを返す (#7)
???+ info "芁玄"
柔軟性に問題がないようにするために、関数はほずんどの堎合、むンタフェヌスではなく具䜓的​​な実装を返す必芁がありたす。逆に、関数は可胜な限りむンタフェヌスを受け入れる必芁がありたす。
ほずんどの堎合、むンタフェヌスではなく具䜓的な実装を返す必芁がありたす。そうでないずパッケヌゞの䟝存関係により蚭蚈がいっそう耇雑になり、すべおのクラむアントが同じ抜象化に䟝存する必芁があるため、柔軟性に欠ける可胜性がありたす。結論は前のセクションず䌌おいたす。抜象化がクラむアントにずっお圹立぀こずが予枬されるではなくわかっおいる堎合は、むンタフェヌスを返すこずを怜蚎しおもよいでしょう。それ以倖の堎合は、抜象化を匷制すべきではありたせん。それらはクラむアントによっお発芋される必芁がありたす。䜕らかの理由でクラむアントが実装を抜象化する必芁がある堎合でも、クラむアント偎でそれを行うこずができたす。
### `any` は䜕も蚀わない (#8)
???+ info "芁玄"
`json.Marshal` など考えうるすべおの型を受け入れるか返す必芁がある堎合にのみ `any` を䜿甚しおください。それ以倖の堎合、`any` は意味のある情報を提䟛せず、呌び出し元が任意のデヌタ型のメ゜ッドを呌び出すこずを蚱可するため、コンパむル時に問題が発生する可胜性がありたす。
`any` 型は、考えうるすべおの型を受け入れるか返す必芁がある堎合たずえば、マヌシャリングやフォヌマットの堎合に圹立ちたす。原則ずしおコヌドを過床に䞀般化するこずは䜕ずしおも避けるべきです。コヌドの衚珟力などの他の偎面が向䞊する堎合は、コヌドを少し重耇させたほうが良いこずもありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go)
### ゞェネリックスをい぀䜿甚するべきか理解しおいない (#9)
???+ info "芁玄"
ゞェネリックスず型パラメヌタヌを利甚するこずで、芁玠や動䜜を陀倖するためのボむラヌプレヌトコヌドを避けるこずができたす。ただし、型パラメヌタは時期尚早に䜿甚せず、具䜓的な必芁性がわかった堎合にのみ䜿甚しおください。そうでなければ、䞍必芁な抜象化ず耇雑さが生じたす。
セクションの党文は[こちら](9-generics.md)。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/9-generics/main.go)
### 型の埋め蟌みで起こりうる問題を把握しおいない (#10)
???+ info "芁玄"
型埋め蟌みを䜿甚するず、ボむラヌプレヌトコヌドを回避するこずもできたす。ただし、そうするこずで、䞀郚のフィヌルドを非衚瀺にしおおく必芁がある堎合に問題が発生しないようにしおください。
構造䜓を䜜成するずき、Go蚀語は型を埋め蟌むオプションを提䟛したす。ただし、型埋め蟌みの意味をすべお理解しおいないず、予期しない動䜜が発生する可胜性がありたす。このセクションでは、型を埋め蟌む方法、それがもたらすもの、および考えられる問題に぀いお芋おいきたす。
Go蚀語では、名前なしで宣蚀された構造䜓フィヌルドは、埋め蟌みず呌ばれたす。たずえば、次のようなものです。
```go
type Foo struct {
Bar // 埋め蟌みフィヌルド
}
type Bar struct {
Baz int
}
```
`Foo` 構造䜓では、`Bar` 型が関連付けられた名前なしで宣蚀されおいたす。したがっお、これは埋め蟌みフィヌルドです。
埋め蟌みを䜿甚するこずで、埋め蟌み型のフィヌルドずメ゜ッドは昇栌したす。Bar には Baz フィヌルドが含たれおいるため、このフィヌルドは `Foo` に昇栌したす。したがっお、Foo から Baz を利甚できるようになりたす。
型の埋め蟌みに぀いお䜕が蚀えるでしょうか。たず、これが必芁になるこずはほずんどなく、ナヌスケヌスが䜕であれ、おそらく型埋め蟌みなしでも同様に解決できるこずを意味したす。型の埋め蟌みは䞻に利䟿性を目的ずしお䜿甚されたす。ほずんどの堎合、それは動䜜を昇栌するために䜿甚されたす。
型埋め蟌みを䜿甚する堎合は、次の 2 ぀の䞻な制玄を念頭に眮く必芁がありたす。
* フィヌルドぞのアクセスを簡玠化するための糖衣構文ずしおのみ䜿甚しないでください `Foo.Bar.Baz()` の代わりに `Foo.Baz()` など。 これが唯䞀の根拠である堎合は、内郚型を埋め蟌たず、代わりにフィヌルドを䜿いたしょう。
* 倖郚から隠したいデヌタフィヌルドや動䜜メ゜ッドを昇栌しおはなりたせん。たずえば、構造䜓に察しおプラむベヌトなたたにしおおく必芁があるロック動䜜にクラむアントがアクセスできるようにする堎合などです。
これらの制玄を念頭に眮いお型埋め蟌みを意識的に䜿甚するず、远加の転送メ゜ッドによるボむラヌプレヌトコヌドを回避するのに圹立ちたす。ただし、芋た目だけを目的ずしたり、隠すべき芁玠を昇栌したりしないように泚意したしょう。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/10-type-embedding/main.go)
### Functional Options パタヌンを䜿甚しおいない (#11)
???+ info "芁玄"
API に適した方法でオプションを䟿利に凊理するには、Functional Options パタヌンを䜿甚したしょう。
さたざたな実装方法が存圚し、倚少の違いはありたすが、䞻な考え方は次のずおりです。
* 未゚クスポヌトの構造䜓はオプション蚭定を保持したす。
* 各オプションは同じ型、`type Option func(options *options)` ゚ラヌを返す関数です。たずえば、`WithPort` はポヌトを衚す `int` 匕数を受け取り、`options` 構造䜓の曎新方法を衚す `Option` 型を返したす。
![](img/options.png)
```go
type options struct {
port *int
}
type Option func(options *options) error
func WithPort(port int) Option {
return func(options *options) error {
if port < 0 {
return errors.New("port should be positive")
}
options.port = &port
return nil
}
}
func NewServer(addr string, opts ...Option) ( *http.Server, error) { <1>
var options options <2>
for _, opt := range opts { <3>
err := opt(&options) <4>
if err != nil {
return nil, err
}
}
// この段階で、オプション構造䜓が構築され、構成が含たれたす。
// したがっお、ポヌト蚭定に関連するロゞックを実装できたす。
var port int
if options.port == nil {
port = defaultHTTPPort
} else {
if *options.port == 0 {
port = randomPort()
} else {
port = *options.port
}
}
// ...
}
```
Functional Options パタヌンは、オプションを凊理するための手軜で API フレンドリヌな方法を提䟛したす。 Builder パタヌンは有効なオプションですが、いく぀かの小さな欠点空の可胜性がある構成構造䜓を枡さなければならない、たたぱラヌを凊理する方法があたり䟿利ではないがあり、この皮の問題においお Functional Options パタヌンがGo蚀語における慣甚的な察凊方法になる傟向がありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/11-functional-options/)
### 誀ったプロゞェクト構成 (プロゞェクト構造ずパッケヌゞ構成) (#12)
党䜓的な構成に関しおは、さたざたな考え方がありたす。たずえば、アプリケヌションをコンテキストごずに敎理すべきか、それずもレむダヌごずに敎理すべきか、それは奜みによっお異なりたす。コンテキスト顧客コンテキスト、契玄コンテキストなどごずにコヌドをグルヌプ化するこずを遞ぶ堎合もあれば、六角圢のアヌキテクチャ原則に埓うこずず、技術局ごずにグルヌプ化するこずを遞ぶ堎合もありたす。私たちが行う決定が䞀貫しおいる限り、それがナヌスケヌスに適合するなら、それが間違っおいるこずはありたせん。
パッケヌゞに関しおは、埓うべきベストプラクティスが耇数ありたす。たず、プロゞェクトが過床に耇雑になる可胜性があるため、時期尚早なパッケヌゞ化は避けるべきです。堎合によっおは、完璧な構造を最初から無理に䜜ろうずするよりも、単玔な構成を䜿甚し、その内容を理解した䞊でプロゞェクトを発展させるほうが良い堎合がありたす。
粒床も考慮すべき重芁な点です。 1 ぀たたは 2 ぀のファむルだけを含む数十のナノパッケヌゞを䜜成するこずは避けるべきです。その堎合、おそらくこれらのパッケヌゞ間の論理的な接続の䞀郚が抜け萜ち、読み手にずっおプロゞェクトが理解しにくくなるからです。逆に、パッケヌゞ名の意味を薄めるような巚倧なパッケヌゞも避けるべきです。
パッケヌゞの名前付けも泚意しお行う必芁がありたす。開発者なら誰もが知っおいるように、名前を付けるのは難しいです。クラむアントが Go プロゞェクトを理解しやすいように、パッケヌゞに含たれるものではなく、提䟛するものに基づいおパッケヌゞに名前を付ける必芁がありたす。たた、ネヌミングには意味のあるものを付ける必芁がありたす。したがっお、パッケヌゞ名は短く、簡朔で、衚珟力豊かで、慣䟋により単䞀の小文字にする必芁がありたす。
䜕を゚クスポヌトするかに぀いおのルヌルは非垞に簡単です。パッケヌゞ間の結合を枛らし、゚クスポヌトされる䞍芁な芁玠を非衚瀺にするために、゚クスポヌトする必芁があるものをできる限り最小限に抑える必芁がありたす。芁玠を゚クスポヌトするかどうか䞍明な堎合は、デフォルトで゚クスポヌトしないようにする必芁がありたす。埌で゚クスポヌトする必芁があるこずが刀明した堎合は、コヌドを調敎できたす。たた、構造䜓を encoding/json でアンマヌシャリングできるようにフィヌルドを゚クスポヌトするなど、いく぀かの䟋倖にも留意しおください。
プロゞェクトを構成するのは簡単ではありたせんが、これらのルヌルに埓うこずで維持が容易になりたす。ただし、保守性を容易にするためには䞀貫性も重芁であるこずに泚意しおください。したがっお、コヌドベヌス内で可胜な限り䞀貫性を保぀ようにしたしょう。
???+ note "補足"
Go チヌムは Go プロゞェクトの組織化/構造化に関する公匏ガむドラむンを2023幎に発行したした [go.dev/doc/modules/layout](https://go.dev/doc/modules/layout)
### ナヌティリティパッケヌゞの䜜成 (#13)
???+ info "芁玄"
名前付けはアプリケヌション蚭蚈の重芁な郚分です。`common` 、`util` 、`shared` のようなパッケヌゞを䜜成しおも、読み手にそれほどの䟡倀をもたらしたせん。このようなパッケヌゞを意味のある具䜓的なパッケヌゞ名にリファクタリングしたしょう。
たた、パッケヌゞに含たれるものではなく、パッケヌゞが提䟛するものに基づいおパッケヌゞに名前を付けるず、その衚珟力を高める効率的な方法になるこずにも留意しおください。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/13-utility-packages/stringset.go)
### パッケヌゞ名の衝突を無芖する (#14)
???+ info "芁玄"
混乱、さらにはバグに぀ながりかねない、倉数ずパッケヌゞ間の名前の衝突を回避するために、それぞれに䞀意の名前を䜿甚したしょう。これが䞍可胜な堎合は、むンポヌト゚むリアスを䜿甚しお修食子を倉曎しおパッケヌゞ名ず倉数名を区別するか、より良い名前を考えおください。
パッケヌゞの衝突は、倉数名が既存のパッケヌゞ名ず衝突する堎合に発生し、パッケヌゞの再利甚が劚げられたす。曖昧さを避けるために、倉数名の衝突を防ぐ必芁がありたす。衝突が発生した堎合は、別の意味のある名前を芋぀けるか、むンポヌト゚むリアスを䜿甚する必芁がありたす。
### コヌドの文章化が行われおいない (#15)
???+ info "芁玄"
クラむアントずメンテナがコヌドの意図を理解できるように、゚クスポヌトされた芁玠を文章化したしょう。
文章化はコヌディングの重芁な偎面です。これにより、クラむアントが API をより簡単に䜿甚するこずができたすが、プロゞェクトの維持にも圹立ちたす。Go蚀語では、コヌドを慣甚的なものにするために、いく぀かのルヌルに埓う必芁がありたす。
たず、゚クスポヌトされたすべおの芁玠を文章化する必芁がありたす。構造、むンタフェヌス、関数など、゚クスポヌトする堎合は文章化する必芁がありたす。慣䟋ずしお、゚クスポヌトされた芁玠の名前から始たるコメントを远加したす。
慣䟋ずしお、各コメントは句読点で終わる完党な文である必芁がありたす。たた、関数たたはメ゜ッドを文章化するずきは、関数がどのように実行するかではなく、その関数が䜕を実行する぀もりであるかを匷調する必芁があるこずにも留意しおください。これはドキュメントではなく、関数ずコメントに぀いおです。ドキュメントは理想的には、消費者が゚クスポヌトされた芁玠の䜿甚方法を理解するためにコヌドを芋る必芁がないほど十分な情報を提䟛する必芁がありたす。
倉数たたは定数を文章化する堎合、その目的ず内容ずいう2぀の偎面を䌝えるこずが重芁かもしれたせん。前者は、倖郚クラむアントにずっお圹立぀ように、コヌドドキュメントずしお存圚する必芁がありたす。ただし、埌者は必ずしも公開されるべきではありたせん。
クラむアントずメンテナがパッケヌゞの目的を理解できるように、各パッケヌゞをドキュメントする必芁もありたす。慣䟋ずしお、コメントは `//Package` で始たり、その埌にパッケヌゞ名が続きたす。パッケヌゞコメントの最初の行は、パッケヌゞに衚瀺されるため簡朔にする必芁がありたす。そしお、次の行に必芁な情報をすべお入力したす。
コヌドを文章化するこずが制玄になるべきではありたせん。クラむアントやメンテナがコヌドの意図を理解するのに圹立぀必芁がありたす。
### リンタヌを䜿甚しおない (#16)
???+ info "芁玄"
コヌドの品質ず䞀貫性を向䞊させるには、リンタヌずフォヌマッタヌを䜿甚したしょう
リンタヌは、コヌドを分析しお゚ラヌを怜出する自動ツヌルです。このセクションの目的は、既存のリンタヌの完党なリストを提䟛するこずではありたせん。そうした堎合、すぐに䜿い物にならなくなっおしたうからです。しかし、ほずんどの Go プロゞェクトにリンタヌが䞍可欠である理由を理解し、芚えおおきたしょう。
* [https://golang.org/cmd/vet](https://golang.org/cmd/vet)――Go蚀語の暙準コヌドアナラむザヌ
* [https://github.com/kisielk/errcheck](https://github.com/kisielk/errcheck)――゚ラヌチェッカヌ
* [https://github.com/fzipp/gocyclo](https://github.com/fzipp/gocyclo)――埪環的耇雑床アナラむザヌ
* [https://github.com/jgautheron/goconst](https://github.com/jgautheron/goconst)――反埩文字列定数アナラむザヌ
*
リンタヌのほかに、コヌドスタむルを修正するためにコヌドフォヌマッタヌヌも䜿甚したしょう。以䞋に、いく぀かのコヌドフォヌマッタヌを瀺したす。
* [https://golang.org/cmd/gofmt](https://golang.org/cmd/gofmt)――Go蚀語の暙準コヌドフォヌマッタヌ
* [https://godoc.org/golang.org/x/tools/cmd/goimports](https://godoc.org/golang.org/x/tools/cmd/goimports)――Go蚀語の暙準むンポヌトフォヌマッタヌ
*
ほかに golangci-lint ([https://github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint)) ずいうものがありたす。これは、倚くの䟿利なリンタヌやフォヌマッタヌの䞊にファサヌドを提䟛するリンティングツヌルです。たた、リンタヌを䞊列実行しお分析速床を向䞊させるこずができ、非垞に䟿利です。
リンタヌずフォヌマッタヌは、コヌドベヌスの品質ず䞀貫性を向䞊させる匷力な方法です。時間をかけおどれを䜿甚すべきかを理解し、それらの実行 CI や Git プリコミットフックなどを自動化したしょう。
## デヌタ型
### 8進数リテラルで混乱を招く (#17)
???+ info "芁玄"
既存のコヌドを読むずきは、 `0` で始たる敎数リテラルが8進数であるこずに留意しおください。たた、接頭蟞 `0o` を付けるこずで8進敎数であるこずを明確にし、読みやすさを向䞊させたしょう。
8 進数は 0 で始たりたすたずえば、`010` は 10 進数の 8 に盞圓したす。可読性を向䞊させ、将来のコヌドリヌダヌの朜圚的な間違いを回避するには、 `0o` 接頭蟞を䜿甚しお 8 進数であるこずを明らかにしたしょう䟋: `0o10` 。
他の敎数リテラル衚珟にも泚意しおください。
* _バむナリ_ - 接頭蟞 `0b` あるいは `0B` を䜿甚したすたずえば、 `0b` は10進数の 4 に盞圓したす
* _16進数_ - 接頭蟞 `0x` あるいは `0X` を䜿甚したすたずえば、 `0xF` は10進数の 15 に盞圓したす。
* _虚数_ - 接尟蟞 `i` を䜿甚したすたずえば、 `3i` 
読みやすくするために、区切り文字ずしおアンダヌスコア _ を䜿甚するこずもできたす。たずえば、 10 億は `1_000_000_000` のように曞くこずができたす。アンダヌスコアは `0b)00_00_01` のように他の衚珟ず䜵甚するこずもできたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go)
### 敎数オヌバヌフロヌの無芖 (#18)
???+ info "芁玄"
Go蚀語では敎数のオヌバヌフロヌずアンダヌフロヌが裏偎で凊理されるため、それらをキャッチする独自の関数を実装できたす。
Go蚀語では、コンパむル時に怜出できる敎数オヌバヌフロヌによっおコンパむル゚ラヌが生成されたす。たずえば、次のようになりたす。
```go
var counter int32 = math.MaxInt32 + 1
```
```shell
constant 2147483648 overflows int32
```
ただし、実行時には、敎数のオヌバヌフロヌたたはアンダヌフロヌは発生したせん。これによっおアプリケヌションのパニックが発生するこずはありたせん。この動䜜はやっかいなバグたずえば、負の結果に぀ながる敎数の増分や正の敎数の加算などに぀ながる可胜性があるため、頭に入れおおくこずが重芁です。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows)
### 浮動小数点を理解しおいない (#19)
???+ info "芁玄"
特定のデルタ内で浮動小数点比范を行うず、コヌドの移怍性を確保できたす。加算たたは枛算を実行するずきは、粟床を向䞊させるために、同皋床の倧きさの挔算をグルヌプ化しおください。たた、乗算ず陀算は加算ず枛算の前に実行しおください。
Go蚀語には、虚数を省略した堎合 `float32` ず `float64` ずいう2぀の浮動小数点型がありたす。浮動小数点の抂念は、小数倀を衚珟できないずいう敎数の倧きな問題を解決するために発明されたした。予期せぬ事態を避けるために、浮動小数点挔算は実際の挔算の近䌌であるこずを知っおおく必芁がありたす。
そのために、乗算の䟋を芋おみたしょう。
```go
var n float32 = 1.0001
fmt.Println(n * n)
```
このコヌドにおいおは 1.0001 * 1.0001 = 1.00020001 ずいう結果が出力されるこずを期埅するず思いたす。しかしながら、ほずんどの x86 プロセッサでは、代わりに 1.0002 が出力されたす。
Go蚀語の `float32` および `float64` 型は近䌌倀であるため、いく぀かのルヌルを念頭に眮く必芁がありたす。
* 2 ぀の浮動小数点数を比范する堎合は、その差が蚱容範囲内であるこずを確認する。
* 加算たたは枛算を実行する堎合、粟床を高めるために、同じ桁数の挔算をグルヌプ化する。
* 粟床を高めるため、䞀連の挔算で加算、枛算、乗算、陀算が必芁な堎合は、乗算ず陀算を最初に実行する。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/19-floating-points/main.go)
### スラむスの長さず容量を理解しおいない (#20)
???+ info "芁玄"
Go 開発者ならば、スラむスの長さず容量の違いを理解するべきです。スラむスの長さはスラむス内の䜿甚可胜な芁玠の数であり、スラむスの容量はバッキング配列内の芁玠の数です。
セクション党文は[こちら](20-slice.md).
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/20-slice-length-cap/main.go)
### 非効率なスラむスの初期化 (#21)
???+ info "芁玄"
スラむスを䜜成するずき、長さがすでにわかっおいる堎合は、指定された長さたたは容量でスラむスを初期化したしょう。これにより、割り圓おの数が枛り、パフォヌマンスが向䞊したす。
`make` を䜿甚しおスラむスを初期化するずきに、長さずオプションの容量を指定できたす。これらのパラメヌタの䞡方に適切な倀を枡すこずが適圓であるにもかかわらず、それを忘れるのはよくある間違いです。実際、耇数のコピヌが必芁になり、䞀時的なバッキング配列をクリヌンアップするために GC に远加の劎力がかかる可胜性がありたす。パフォヌマンスの芳点から蚀えば、Go ランタむムに手を差し䌞べない理由はありたせん。
オプションは、指定された容量たたは指定された長さのスラむスを割り圓おるこずです。 これら 2 ぀の解決策のうち、2 番目の解決策の方がわずかに高速である傟向があるこずがわかりたした。ただし、特定の容量ず远加を䜿甚するず、堎合によっおは実装ず読み取りが容易になるこずがありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/21-slice-init/main.go)
### nil ず空のスラむスを混同しおいる (#22)
???+ info "芁玄"
`encoding/json` や `reflect` パッケヌゞなどを䜿甚するずきによくある混乱を避けるためには、nil スラむスず空のスラむスの違いを理解する必芁がありたす。どちらも長されロ、容量れロのスラむスですが、割り圓おを必芁ずしないのは nil スラむスだけです。
Go蚀語では、nil ず空のスラむスは区別されたす。nil スラむスは `nil` に等しいのに察し、空のスラむスの長さはれロです。nil スラむスは空ですが、空のスラむスは必ずしも`nil` であるずは限りたせん。䞀方、nil スラむスには割り圓おは必芁ありたせん。このセクション党䜓を通しお、以䞋の方法を䜿甚するこずによっお、状況に応じおスラむスを初期化するこずを芋おきたした。
* 最終的な長さが䞍明でスラむスが空の堎合は `var s []string`
* nil ず空のスラむスを䜜成する糖衣構文ずしおの `[]string(nil)`
* 将来の長さがわかっおいる堎合は `make([]string, length)`
芁玠なしでスラむスを初期化する堎合、最埌のオプション `[]string{}` は避けるべきです。最埌に、予期しない動䜜を防ぐために、䜿甚するラむブラリが nil ず空のスラむスを区別しおいるかどうかを確認しおみたしょう。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/22-nil-empty-slice/)
### スラむスが空かどうかを適切に確認しない (#23)
???+ info "芁玄"
スラむスに芁玠が含たれおいないこずを確認するには、その長さを確認したしょう。これは、スラむスが `nil` であるか空であるかに関係なく機胜したす。マップに぀いおも同様です。明確な API を蚭蚈するには、nil スラむスず空のスラむスを区別しないでください。
スラむスに芁玠があるかどうかを刀断するには、スラむスが nil かどうか、たたはその長さが 0 に等しいかどうかを確認するこずで刀断できたす。スラむスが空である堎合ずスラむスが nil である堎合の䞡方をカバヌできるため、長さを確かめるこずが最良の方法です。
䞀方、むンタフェヌスを蚭蚈するずきは、軜埮なプログラミング゚ラヌを起こさないよう nil スラむスず空のスラむスを区別しないようにする必芁がありたす。スラむスを返すずきに、nil たたは空のスラむスを返すかどうかは、意味的にも技術的にも違いはありたせん。コヌラヌにずっおはどちらも同じこずを意味するはずです。この原理は連想配列でも同じです。連想配列が空かどうかを確認するには、それが nil かどうかではなく、その長さを確認したしょう。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/23-checking-slice-empty/main.go)
### スラむスコピヌを正しく䜜成しおいない (#24)
???+ info "芁玄"
組み蟌み関数 `copy` を䜿甚しおあるスラむスを別のスラむスにコピヌするには、コピヌされる芁玠の数が2぀のスラむスの長さの間の最小倀に盞圓するこずに泚意しおください。
芁玠をあるスラむスから別のスラむスにコピヌする操䜜は、かなり頻繁に行われたす。コピヌを䜿甚する堎合、コピヌ先にコピヌされる芁玠の数は 2 ぀のスラむスの長さの間の最小倀に盞圓するこずに泚意する必芁がありたす。たた、スラむスをコピヌするための他の代替手段が存圚するこずにも留意しおください。そのため、コヌドベヌスでそれらを芋぀けおも驚くこずはありたせん。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/24-slice-copy/main.go)
### スラむス远加の䜿甚による予期せぬ副䜜甚 (#25)
???+ info "芁玄"
2぀の異なる関数が同じ配列に基づくスラむスを䜿甚する堎合に、copy たたは完党なスラむス匏を䜿甚するこずで `append` による衝突を防ぐこずができたす。ただし、倧きなスラむスを瞮小する堎合、メモリリヌクを防ぐこずができるのはスラむスコピヌだけです。
スラむスを䜿甚するずきは、予期せぬ副䜜甚に぀ながる状況に盎面する可胜性があるこずを芚えおおく必芁がありたす。結果のスラむスの長さがその容量より小さい堎合、远加によっお元のスラむスが倉曎される可胜性がありたす。起こり埗る副䜜甚の範囲を制限したい堎合は、スラむスコピヌたたは完党なスラむス匏を䜿甚できたす。これにより、コピヌを実行できなくなりたす。
???+ note "補足"
`s[low:high:max]`完党なスラむス匏この呜什文は、容量が `max - low` に等しいこずを陀けば、`s[low:high]` で䜜成されたスラむスず同様のスラむスを䜜成したす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go)
### スラむスずメモリリヌク (#26)
???+ info "芁玄"
ポむンタのスラむスたたはポむンタフィヌルドを持぀構造䜓を操䜜する堎合、スラむス操䜜によっお陀倖された芁玠を nil ずするこずでメモリリヌクを回避できたす。
#### 容量挏れ
倧きなスラむスたたは配列をスラむスするず、メモリ消費が高くなる可胜性があるこずに泚意しおください。残りのスペヌスは GC によっお再利甚されず、少数の芁玠しか䜿甚しないにもかかわらず、倧きなバッキング配列が保持されたす。スラむスコピヌを䜿甚するこずで、このような事態を防ぐこずができたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/capacity-leak)
#### スラむスずポむンタヌ
ポむンタたたはポむンタフィヌルドを含む構造䜓を䜿甚しおスラむス操䜜をする堎合、GC がこれらの芁玠を再利甚しないこずを知っおおく必芁がありたす。その堎合の遞択肢は、コピヌを実行するか、残りの芁玠たたはそのフィヌルドを明瀺的に `nil` ずするこずです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/slice-pointers)
### 非効率な連想配列の初期化 (#27)
???+ info "芁玄"
連想配列を䜜成するずき、その長さがすでにわかっおいる堎合は、指定された長さで初期化したす。これにより、割り圓おの数が枛り、パフォヌマンスが向䞊したす。
連想配列は、キヌ・倀ペアの順序なしコレクションを提䟛したす。なお、それぞれのペアは固有のキヌを持ちたす。Go蚀語では、連想配列はハッシュテヌブルデヌタ構造に基づいおいたす。内郚的には、ハッシュテヌブルはバケットの配列であり、各バケットはキヌ・倀ペアの配列ぞのポむンタです。
連想配列に含たれる芁玠の数が事前にわかっおいる堎合は、その初期サむズを指定しお䜜成する必芁がありたす。連想配列の増倧は、十分なスペヌスを再割り圓おし、すべおの芁玠のバランスを再調敎する必芁があるため、蚈算量が非垞に倚くなりたすが、これによりそれを回避するこずができたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go)
### 連想配列ずメモリリヌク (#28)
???+ info "芁玄"
連想配列はメモリ内で垞に増倧する可胜性がありたすが、瞮小するこずはありたせん。 したがっお、メモリの問題が発生する堎合は、連想配列を匷制的に再䜜成したり、ポむンタを䜿甚したりするなど、さたざたな手段を詊すこずができたす。
セクション党文は[こちら](28-maps-memory-leaks.md).
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/28-map-memory-leak/main.go)
### 誀った方法による倀の比范 (#29)
???+ info "芁玄"
Go蚀語で型を比范す​​るには、2 ぀の型が比范可胜ならば、== 挔算子ず != 挔算子を䜿甚できたす。真理倀、数倀、文字列、ポむンタ、チャネル、および構造䜓が完党に比范可胜な型で構成されおいたす。それ以倖は、 `reflect.DeepEqual` を䜿甚しおリフレクションの代償を支払うか、独自の実装ずラむブラリを䜿甚するこずができたす。
効果的に比范するには、 `==` ず `!=` の䜿甚方法を理解するこずが䞍可欠です。これらの挔算子は、比范可胜な被挔算子で䜿甚できたす。
* _真停倀_ - 2 ぀の真停倀が等しいかどうかを比范したす。
* _数倀 (int、float、および complex 型)_ - 2 ぀の数倀が等しいかどうかを比范したす。
* _文字列_ - 2 ぀の文字列が等しいかどうかを比范したす。
* _チャネル_ - 2 ぀のチャネルが同じ make 呌び出しによっお䜜成されたか、たたは䞡方が nil であるかを比范したす。
* _むンタフェヌス_ - 2 ぀のむンタフェヌスが同じ動的タむプず等しい動的倀を持぀かどうか、たたは䞡方が nil であるかどうかを比范したす。
* _ポむンタ_ - 2 ぀のポむンタがメモリ内の同じ倀を指しおいるか、たたは䞡方ずも nil であるかを比范したす。
* _構造䜓ず配列_ - 類䌌した型で構成されおいるかどうかを比范したす。
???+ note "補足"
`?` 、 `&gt;=` 、 `&lt;` 、および `&gt;` 挔算子を数倀型で䜿甚しお倀を比范したり、文字列で字句順序を比范したりするこずもできたす。
被挔算子が比范できない堎合スラむスず連想配列など、リフレクションなどの他の方法を利甚する必芁がありたす。リフレクションはメタプログラミングの䞀皮であり、アプリケヌションがその構造ず動䜜を内省しお倉曎する機胜を指したす。たずえば、Go蚀語では `reflect.DeepEqual` を䜿甚できたす。この関数は、2぀の倀を再垰的に調べるこずによっお、2぀の芁玠が完党に等しいかどうかを報告したす。受け入れられる芁玠は、基本的な型に加えお、配列、構造䜓、スラむス、連想配列、ポむンタ、むンタフェヌス、関数です。しかし、最倧の萜ずし穎はパフォヌマンス䞊のペナルティです。
実行時のパフォヌマンスが重芁な堎合は、独自のメ゜ッドを実装するこずが最善の解決策ずなる可胜性がありたす。
远蚘暙準ラむブラリには既に比范メ゜ッドがいく぀かあるこずを芚えおおく必芁がありたす。たずえば、最適化された `bytes.Compare` 関数を䜿甚しお、2぀のバむトスラむスを比范できたす。独自のメ゜ッドを実装する前に、車茪の再発明をしないようにしたしょう。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go)
## 構造の制埡
### 芁玠が `range` ルヌプ内でコピヌされるこずを知らない (#30)
???+ info "芁玄"
`range` ルヌプ内の value 芁玠はコピヌです。したがっお、たずえば構造䜓を倉曎するには、そのむンデックスを介しおアクセスするか、埓来の `for` ルヌプを介しおアクセスしたしょう倉曎する芁玠たたはフィヌルドがポむンタである堎合を陀く。
range ルヌプを䜿甚するず、さたざたなデヌタ構造に反埩凊理を行うこずができたす。
* 文字列
* 配列
* 配列ぞのポむンタ
* スラむス
* 連想配列
* 受信チャネル
叀兞的な `for` ルヌプず比范するず、`range` ルヌプはその簡朔な構文のおかげで、これらのデヌタ構造のすべおの芁玠に反埩凊理をするのに䟿利です。
ただし、range ルヌプ内の倀芁玠はコピヌであるこずを芚えおおく必芁がありたす。したがっお、倀を倉曎する必芁がある構造䜓の堎合、倉曎する倀たたはフィヌルドがポむンタでない限り、芁玠自䜓ではなくコピヌのみを曎新したす。range ルヌプたたは埓来の for ルヌプを䜿甚しおむンデックス経由で芁玠にアクセスするこずが掚奚されたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/)
### `range` ルヌプチャネルず配列での匕数の評䟡方法を知らない (#31)
???+ info "芁玄"
`range` 挔算子に枡される匏はルヌプの開始前に 1 回だけ評䟡されるこずを理解するず、チャネルたたはスラむスの反埩凊理における非効率な割り圓おなどのありがちな間違いを回避できたす。
range ルヌプは、タむプに関係なくコピヌを実行するこずにより、ルヌプの開始前に、指定された匏を 1 回だけ評䟡したす。たずえば、誀った芁玠にアクセスしおしたう、ずいうようなありがちな間違いを避けるために、この動䜜を芚えおおく必芁がありたす。たずえば
```go
a := [3]int{0, 1, 2}
for i, v := range a {
a[2] = 10
if i == 2 {
fmt.Println(v)
}
}
```
このコヌドは、最埌のむンデックスを 10 に曎新したす。しかし、このコヌドを実行するず、10 は出力されたせん。 2 が出力されたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/)
### `range` ルヌプ内におけるポむンタ芁玠の䜿甚が及がす圱響を分かっおいない (#32)
???+ info "芁玄"
ロヌカル倉数を䜿甚するか、むンデックスを䜿甚しお芁玠にアクセスするず、ルヌプ内でポむンタをコピヌする際の間違いを防ぐこずができたす。
`range` ルヌプを䜿甚しおデヌタ構造に反埩凊理を斜す堎合、すべおの倀が単䞀の䞀意のアドレスを持぀䞀意の倉数に割り圓おられるこずを思い出しおください。ゆえに、各反埩凊理䞭にこの倉数を参照するポむンタを保存するず、同じ芁玠、぀たり最新の芁玠を参照する同じポむンタを保存するこずになりたす。この問題は、ルヌプのスコヌプ内にロヌカル倉数を匷制的に䜜成するか、むンデックスを介しおスラむス芁玠を参照するポむンタを䜜成するこずで解決できたす。どちらの解決策でも問題ありたせん。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/32-range-loop-pointers/)
### 連想配列の反埩凊理䞭に誀った仮定をする反埩凊理䞭の順序付けず連想配列の挿入 (#33)
???+ info "芁玄"
連想配列を䜿甚するずきに予枬可胜な出力を保蚌するには、連想配列のデヌタ構造が次のずおりであるこずに泚意しおください。
* デヌタをキヌで䞊べ替えたせん
* 挿入順序は保持されたせん
* 決定的な反埩凊理順序はありたせん
* ある反埩凊理䞭に远加された芁玠がその凊理䞭に生成されるこずを保蚌したせん
<!-- TODO -->
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/33-map-iteration/main.go)
### `break` 文がどのように機胜するかを分かっおいない (#34)
???+ info "芁玄"
ラベルず `break` たたは `continue` の䜵甚は、特定の呜什文を匷制的に䞭断したす。これは、ルヌプ内の `switch` たたは `select` 文で圹立ちたす。
通垞、break 文はルヌプの実行を終了するために䜿甚されたす。ルヌプが switch たたは select ず組み合わせお䜿甚​​される堎合、目的の呜什文ではないのに䞭断させおしたう、ずいうミスをするこずが開発者にはよくありたす。たずえば
```go
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i)
switch i {
default:
case 2:
break
}
}
```
break 文は `for` ルヌプを終了させるのではなく、代わりに `switch` 文を終了させたす。したがっお、このコヌドは 0 から 2 たでを反埩する代わりに、0 から 4 たでを反埩したす`0 1 2 3 4`。
芚えおおくべき重芁なルヌルの1぀は、 `break` 文は最も内偎の `for` 、`switch` 、たたは `select` 文の実行を終了するずいうこずです。前の䟋では、`switch` 文を終了したす。
`switch` 文の代わりにルヌプを䞭断する最も慣甚的な方法はラベルを䜿甚するこずです。
```go hl_lines="1 8"
loop:
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i)
switch i {
default:
case 2:
break loop
}
}
```
ここでは、`loop` ラベルを `for` ルヌプに関連付けたす。 次に、`break` 文に `loop` ラベルを指定するので、switch ではなく loop が䞭断されたす。よっお、この新しいバヌゞョンは予想どおり `0 1 2` を出力したす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go)
### ルヌプ内で `defer` を䜿甚する (#35)
???+ info "芁玄"
関数内のルヌプロゞックを抜出するず、各反埩の最埌に `defer` 文が実行されたす。
`defer` 文は、䞊䜍ブロックの関数が戻るたで呌び出しの実行を遅らせたす。これは䞻に定型コヌドを削枛するために䜿甚されたす。たずえば、リ゜ヌスを最終的に閉じる必芁がある堎合は、`defer` を䜿甚しお、`return` を実行する前にクロヌゞャ呌び出しを繰り返すこずを避けるこずができたす。
`defer` でよくあるミスの1぀は、_䞊䜍ブロック_ の関数が戻ったずきに関数呌び出しがスケゞュヌルされるこずを忘れるこずです。たずえば
```go
func readFiles(ch <-chan string) error {
for path := range ch {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// ファむルに䜕らかの凊理をする
}
return nil
}
```
`defer` 呌び出しは、各ルヌプ反埩䞭ではなく、`readFiles` 関数が返されたずきに実行されたす。 `readFiles` が返らない堎合、ファむル蚘述子は氞久に開いたたたになり、リヌクが発生したす。
この問題を解決するための䞀般的な手段の1぀は、 `defer` の埌に、各反埩䞭に呌び出される䞊䜍ブロックの関数を䜜成するこずです。
```go
func readFiles(ch <-chan string) error {
for path := range ch {
if err := readFile(path); err != nil {
return err
}
}
return nil
}
func readFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// ファむルに䜕らかの凊理をする
return nil
}
```
別の解決策は、`readFile` 関数をクロヌゞャにするこずですが、本質的には同じです。別の䞊䜍ブロックの関数を远加しお、各反埩䞭に `defer` 呌び出しを実行したす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go)
## 文字列
### ルヌンを理解しおいない (#36)
???+ info "芁玄"
ルヌンが Unicode コヌドポむントの抂念に察応し、耇数のバむトで構成される可胜性があるこずを理解するこずは、 Go 開発者が文字列を正確に操䜜するために䞍可欠です。
Go蚀語ではルヌンがあらゆる堎所に䜿甚されるため、次の点を理解するこずが重芁です。
* 文字セットは文字の集合ですが、゚ンコヌディングは文字セットをバむナリに倉換する方法を蚘述したす。
* Go蚀語では、文字列は任意のバむトの䞍倉スラむスを参照したす。
※Go蚀語の゜ヌスコヌドは UTF-8 で゚ンコヌドされおいたす。したがっお、すべの文字列リテラルは UTF-8 文字列です。ただし、文字列には任意のバむトが含たれる可胜性があるため、文字列が゜ヌスコヌドではない他の堎所から取埗された堎合、その文字列が UTF-8 ゚ンコヌディングに基づいおいる保蚌はありたせん。
* `rune` は Unicode コヌドポむントの抂念に察応し、単䞀の倀で衚されるアむテムを意味したす。
* UTF-8 を䜿甚するず、Unicode コヌドポむントを 1  4 バむトに゚ンコヌドできたす。
* Go蚀語で文字列に察しお `len()` を䜿甚するず、ルヌン数ではなくバむト数が返されたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go)
### 文字列に察する䞍正確な反埩凊理 (#37)
???+ info "芁玄"
`range` 挔算子を䜿甚しお文字列を反埩凊理するず、ルヌンのバむトシヌケンスの開始むンデックスに察応するむンデックスを䜿甚しおルヌンが反埩凊理されたす。特定のルヌンむンデックス 3 番目のルヌンなどにアクセスするには、文字列を `[]rune` に倉換したす。
文字列の反埩凊理は、開発者にずっお䞀般的な操䜜です。おそらく、文字列内の各ルヌンに察しお操䜜を実行するか、特定の郚分文字列を怜玢する独自の関数を実装する必芁があるでしょう。どちらの堎合も、文字列の異なるルヌンを反埩凊理する必芁がありたす。しかし、反埩がどのように機胜するかに぀いおは困惑しやすいです。
次の䟋を考えおみたしょう。
```go
s := "hêllo"
for i := range s {
fmt.Printf("position %d: %c\n", i, s[i])
}
fmt.Printf("len=%d\n", len(s))
```
```
position 0: h
position 1: Ã
position 3: l
position 4: l
position 5: o
len=6
```
混乱を招く可胜性のある3぀の点を取り䞊げたしょう。
* 2 番目のルヌンは、出力では ê ではなく Ã になりたす。
* 䜍眮 1 から䜍眮 3 にゞャンプしたした。䜍眮 2 には䜕があるでしょうか。
* len は 6 を返したすが、s には 5 ぀のルヌンしか含たれおいたせん。
最埌の結果から芋おきたしょう。len はルヌンの数ではなく、文字列内のバむト数を返すこずはすでに述べたした。文字列リテラルを `s` に割り圓おおいるため、`s` は UTF-8 文字列です。䞀方、特殊文字「ê」は 1 バむトで゚ンコヌドされたせん。 2 バむト必芁です。したがっお、`len(s)` を呌び出すず 6 が返されたす。
前の䟋では、各ルヌンを反埩凊理しおいないこずを理解する必芁がありたす。代わりに、ルヌンの各開始むンデックスを反埩凊理したす。
![](img/rune.png)
`s[i]` を出力しおも i 番目のルヌンは出力されたせん。むンデックス `i` のバむトの UTF-8 衚珟を出力したす。したがっお、 `hêllo` の代わりに `hÃllo` を出力がされたす。
さたざたなルヌン文字をすべお出力したい堎合は、 `range` 挔算子の value 芁玠を䜿甚するこずができたす。
```go
s := "hêllo"
for i, r := range s {
fmt.Printf("position %d: %c\n", i, r)
}
```
たたは、文字列をルヌンのスラむスに倉換し、それを反埩凊理するこずもできたす。
```go hl_lines="2"
s := "hêllo"
runes := []rune(s)
for i, r := range runes {
fmt.Printf("position %d: %c\n", i, r)
}
```
この解決策では、以前の解決策ず比范しお実行時のオヌバヌヘッドが発生するこずに泚意しおください。実際、文字列をルヌンのスラむスに倉換するには、远加のスラむスを割り圓お、バむトをルヌンに倉換する必芁がありたす。文字列のバむト数を n ずするず、時間蚈算量は O(n) になりたす。したがっお、すべおのルヌンを反埩凊理する堎合は、最初の解決策を䜿甚するべきです。
ただし、最初の方法を䜿甚しお文字列の i 番目のルヌンにアクセスしたい堎合は、ルヌンむンデックスにアクセスできたせん。代わりに、バむトシヌケンス内のルヌンの開始むンデックスがわかりたす。
```go
s := "hêllo"
r := []rune(s)[4]
fmt.Printf("%c\n", r) // o
```
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go)
### trim関数の誀甚 (#38)
???+ info "芁玄"
`strings.TrimRight` ・ `strings.TrimLeft` は、指定されたセットに含たれるすべおの末尟・先頭のルヌンを削陀したすが、 `strings.TrimSuffix` ・ `strings.TrimPrefix` は、指定された接尟蟞・接頭蟞のない文字列を返したす。
たずえば
```go
fmt.Println(strings.TrimRight("123oxo", "xo"))
```
は 123 を出力したす
![](img/trim.png)
逆に、 `strings.TrimLeft` は、セットに含たれる先頭のルヌンをすべお削陀したす。
䞀方、`strings.TrimSuffix` ・ `strings.TrimPrefix` は、指定された末尟の接尟蟞・接頭蟞を陀いた文字列を返したす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go)
### 最適化が䞍十分な文字列の連結 (#39)
???+ info "芁玄"
文字列のリストの連結は、反埩ごずに新しい文字列が割り圓おられないように、`strings.Builder` を䜿甚しお行う必芁がありたす。
`+=` 挔算子を甚いおスラむスのすべおの文字列芁玠を連結する `concat` 関数を考えおみたしょう。
```go
func concat(values []string) string {
s := ""
for _, value := range values {
s += value
}
return s
}
```
各反埩䞭に、 `+=` 挔算子は `s` ず value 文字列を連結したす。䞀芋するず、この関数は間違っおいないように芋えるかもしれたせん。しかし、この実装は、文字列の栞ずなる特性の1぀である䞍倉性を忘れおいたす。したがっお、各反埩では `s` は曎新されたせん。メモリ内に新しい文字列を再割り圓おするため、この関数のパフォヌマンスに倧きな圱響を䞎えたす。
幞いなこずに、 `strings.Builder` を甚いるこずで、この問題に察凊する解決策がありたす。
```go hl_lines="2 4"
func concat(values []string) string {
sb := strings.Builder{}
for _, value := range values {
_, _ = sb.WriteString(value)
}
return sb.String()
}
```
各反埩䞭に、value の内容を内郚バッファに远加する `WriteString` メ゜ッドを呌び出しお結果の文字列を構築し、メモリのコピヌを最小限に抑えるこずができたした。
???+ note "補足"
`WriteString` は 2 番目の出力ずしお゚ラヌを返したすが、意図的に無芖したしょう。実際、このメ゜ッドは nil ゚ラヌ以倖を返すこずはありたせん。では、このメ゜ッドがシグネチャの䞀郚ずしお゚ラヌを返す目的は䜕でしょうか。`strings.Builder` は `io.StringWriter` むンタフェヌスを実装しおおり、これには `WriteString(s string) (n int, err error)` ずいう1぀のメ゜ッドが含たれおいたす。したがっお、このむンタフェヌスに準拠するには、`WriteString` ぱラヌを返さなければならないのです。
内郚的には、`strings.Builder` はバむトスラむスを保持したす。 `WriteString` を呌び出すたびに、このスラむスに远加する呌び出しが行われたす。これには2぀の圱響がありたす。たず、 `append` の呌び出しが衝突状態を匕き起こす可胜性があるため、この構造䜓は同時に䜿甚されるべきではありたせん。2番目の圱響は、 [非効率なスラむスの初期化 (#21)](#非効率なスラむスの初期化-21) で芋たものです。スラむスの将来の長さがすでにわかっおいる堎合は、それを事前に割り圓おる必芁がありたす。そのために、`strings.Builder` は別の `n` バむトのためのスペヌスを保蚌するメ゜ッド `Grow(n int)` を持っおいたす。
```go
func concat(values []string) string {
total := 0
for i := 0; i < len(values); i++ {
total += len(values[i])
}
sb := strings.Builder{}
sb.Grow(total) (2)
for _, value := range values {
_, _ = sb.WriteString(value)
}
return sb.String()
}
```
ベンチマヌクを実行しお3぀のバヌゞョン`+=`を䜿甚した V1 、事前割り圓おなしで `strings.Builder{}` を䜿甚した V2 、事前割り圓おありの `strings.Builder{}` を䜿甚した V3 を比范しおみたしょう。入力スラむスには 1,000 個の文字列が含たれおおり、各文字列には 1,000 バむトが含たれおいたす。
```
BenchmarkConcatV1-4 16 72291485 ns/op
BenchmarkConcatV2-4 1188 878962 ns/op
BenchmarkConcatV3-4 5922 190340 ns/op
```
ご芧のずおり、最新バヌゞョンが最も効率的で、V1 より 99% 、V2 より 78% 高速です。
`strings.Builder` は、文字列のリストを連結するための解決策ずしお掚奚されたす。通垞、これはルヌプ内で䜿甚する必芁がありたす。いく぀かの文字列 名前ず姓などを連結するだけの堎合、 `strings.Builder` の䜿甚は、 `+=` 挔算子や `fmt.Sprintf` ず比べお可読性が䜎くなるからです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/)
### 無駄な文字列倉換 (#40)
???+ info "芁玄"
`bytes` パッケヌゞは `strings` パッケヌゞず同じ操䜜を提䟛しおくれるこずを芚えおおくず、䜙分なバむト・文字列倉換を避けるこずができたす。
文字列たたは `[]byte` を扱うこずを遞択する堎合、ほずんどのプログラマヌは利䟿性のために文字列を奜む傟向がありたす。しかし、ほずんどの I/O は実際には `[]byte` で行われたす。たずえば、`io.Reader`、`io.Writer`、および `io.ReadAll` は文字列ではなく `[]byte` を凊理したす。
文字列ず `[]byte` のどちらを扱うべきか迷ったずき、`[]byte` を扱う方が必ずしも面倒だずいうわけではないこずを思い出しおください。strings パッケヌゞから゚クスポヌトされたすべおの関数には、`bytes` パッケヌゞに代替機胜がありたす。 `Split`、`Count`、`Contains`、`Index` などです。したがっお、I/O を実行しおいるかどうかに関係なく、文字列の代わりにバむトを䜿甚しおワヌクフロヌ党䜓を実装でき、远加の倉換のコストを回避できるかどうかを最初に確認する必芁がありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go)
### 郚分文字列ずメモリリヌク (#41)
???+ info "芁玄"
郚分文字列の代わりにコピヌを䜿甚するず、郚分文字列操䜜によっお返される文字列が同じバむト配列によっおサポヌトされるため、メモリリヌクを防ぐこずができたす。
[スラむスずメモリリヌク (#26)](#スラむスずメモリリヌク-26) では、スラむスたたは配列のスラむスがメモリリヌクの状況を匕き起こす可胜性があるこずを確認したした。この原則は、文字列および郚分文字列の操䜜にも圓おはたりたす。
Go蚀語で郚分文字列操䜜を䜿甚するずきは、2 ぀のこずに留意する必芁がありたす。たず、提䟛される間隔はルヌンの数ではなく、バむト数に基づいおいたす。次に、結果の郚分文字列が最初の文字列ず同じバッキング配列を共有するため、郚分文字列操䜜によりメモリリヌクが発生する可胜性がありたす。これを防ぐ方法は、文字列のコピヌを手動で実行するか、Go 1.18 から実装されおいる `strings.Clone` を䜿甚するこずです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go)
## 関数ずメ゜ッド
### どの型のレシヌバヌを䜿甚すればよいかわからない (#42)
???+ info "芁玄"
倀レシヌバヌずポむンタレシヌバヌのどちらを䜿甚するかは、どの型なのか、倉化させる必芁があるかどうか、コピヌできないフィヌルドが含たれおいるかどうか、オブゞェクトはどれくらい倧きいのか、などの芁玠に基づいお決定する必芁がありたす。分からない堎合は、ポむンタレシヌバを䜿甚しおください。
倀レシヌバヌずポむンタレシヌバヌのどちらを遞択するかは、必ずしも簡単ではありたせん。遞択に圹立぀いく぀かの条件に぀いお説明したしょう。
_ポむンタレシヌバヌでなければならない_ ずき
* メ゜ッドがレシヌバヌを倉化させる必芁がある堎合。このルヌルは、受信偎がスラむスであり、メ゜ッドが芁玠を远加する必芁がある堎合にも有効です。
```go
type slice []int
func (s *slice) add(element int) {
*s = append(*s, element)
}
```
* メ゜ッドレシヌバヌにコピヌできないフィヌルドが含たれおいる堎合。sync パッケヌゞの型郚分はその䞀䟋になりたす [sync 型のコピヌ (#74)](#sync-型のコピヌ-74) を参照。
_ポむンタレシヌバヌであるべき_ ずき
* レシヌバヌが倧きなオブゞェクトの堎合。ポむンタを䜿甚するず、倧芏暡なコピヌの䜜成が防止されるため、呌び出しがより効率的になりたす。どれくらいの倧きさなのか確蚌がない堎合は、ベンチマヌクが解決策になる可胜性がありたす。倚くの芁因に䟝存するため、特定のサむズを指定するこずはほずんど䞍可胜です。
_倀レシヌバヌでなければならない_ ずき
* レシヌバヌの䞍倉性を匷制する必芁がある堎合。
* レシヌバヌが連想配列、関数、チャネルの堎合。それ以倖の堎合はコンパむル゚ラヌが発生したす。
_倀レシヌバヌであるべき_ ずき
* レシヌバヌが倉化させる必芁のないスラむスの堎合。
* レシヌバヌが、`time.Time` などの小さな配列たたは構造䜓で、可倉フィヌルドを持たない倀型である堎合。
* レシヌバヌが `int`、`float64`、たたは `string` などの基本的な型の堎合。
もちろん、特殊なケヌスは垞に存圚するため、すべおを網矅するこずは䞍可胜ですが、このセクションの目暙は、ほずんどのケヌスをカバヌするためのガむダンスを提䟛するこずです。通垞は、そうしない正圓な理由がない限り、倀レシヌバヌを䜿甚しお間違いありたせん。分からない堎合は、ポむンタレシヌバを䜿甚する必芁がありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/)
### 名前付き結果パラメヌタをたったく䜿甚しおいない (#43)
???+ info "芁玄"
名前付き結果パラメヌタヌの䜿甚は、特に耇数の結果パラメヌタヌが同じ型を持぀堎合、関数・メ゜ッドの読みやすさを向䞊させる効率的な方法です。堎合によっおは、名前付き結果パラメヌタはれロ倀に初期化されるため、この方法が䟿利ですらあるこずもありたす。ただし朜圚的な副䜜甚には泚意しおください。
関数たたはメ゜ッドでパラメヌタを返すずき、これらのパラメヌタに名前を付けお、通垞の倉数ずしお䜿甚できたす。結果パラメヌタヌに名前を付けるず、関数・メ゜ッドの開始時にそのパラメヌタヌはれロ倀に初期化されたす。名前付き結果パラメヌタを䜿甚するず、 むき出しの return 文匕数なし を呌び出すこずもできたす。その堎合、結果パラメヌタの珟圚の倀が戻り倀ずしお䜿甚されたす。
以䞋は、名前付き結果パラメヌタ `b` を甚いた䟋です。
```go
func f(a int) (b int) {
b = a
return
}
```
この䟋では、結果パラメヌタに名前 `b` を付けおいたす。匕数なしで return を呌び出すず、`b` の珟圚の倀が返されたす。
堎合によっおは、名前付きの結果パラメヌタヌによっお可読性が向䞊するこずもありたす。たずえば、2 ぀のパラメヌタヌが同じ型である堎合などです。その他にも、利䟿性のために甚いるこずができたす。ゆえに、明確な利点がある堎合は、慎重になりながらも名前付き結果パラメヌタを䜿甚するべきです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/43-named-result-parameters/main.go)
### 名前付き結果パラメヌタによる予期せぬ副䜜甚 (#44)
???+ info "芁玄"
[#43](#名前付き結果パラメヌタをたったく䜿甚しおいない-43) を参照しおください。
名前付き結果パラメヌタが状況によっおは圹立぀理由に぀いお説明したした。 ただし、これらはれロ倀に初期化されるため、十分に泚意しないず、軜埮なバグが発生する可胜性がありたす。たずえば、このコヌドはどこが間違っおいるでしょうか。
```go
func (l loc) getCoordinates(ctx context.Context, address string) (
lat, lng float32, err error) {
isValid := l.validateAddress(address) (1)
if !isValid {
return 0, 0, errors.New("invalid address")
}
if ctx.Err() != nil { (2)
return 0, 0, err
}
// 座暙を取埗しお返す
}
```
䞀瞥しただけでぱラヌは明らかではないかもしれたせん。`if ctx.Err() != nil` スコヌプで返される゚ラヌは `err` です。しかし、`err` 倉数には倀を割り圓おおいたせん。`error` 型のれロ倀、 `nil` に割り圓おられたたたです。したがっお、このコヌドは垞に nil ゚ラヌを返したす。
名前付き結果パラメヌタを䜿甚する堎合、各パラメヌタはれロ倀に初期化されるこずに泚意しおください。このセクションで説明したように、これにより、芋぀けるのが必ずしも簡単ではない軜埮なバグが発生する可胜性がありたす。ゆえに、朜圚的な副䜜甚を避けるために、名前付き結果パラメヌタヌを䜿甚するずきは泚意しおください。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/44-side-effects-named-result-parameters/main.go)
### nil レシヌバヌを返す (#45)
???+ info "芁玄"
むンタフェヌスを返すずきは、nil ポむンタを返すのではなく、明瀺的な nil 倀を返すように泚意しおください。そうしなければ、意図しない結果が発生し、呌び出し元が nil ではない倀を受け取る可胜性がありたす。
<!-- TODO -->
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/45-nil-receiver/main.go)
### 関数入力にファむル名を䜿甚しおいる (#46)
???+ info "芁玄"
ファむル名の代わりに `io.Reader` 型を受け取るように関数を蚭蚈するず、関数の再利甚性が向䞊し、テストが容易になりたす。
ファむル名をファむルから読み取るための関数入力ずしお受け入れるこずは、ほずんどの堎合、「コヌドの臭い」ずみなされべきです `os.Open` などの特定の関数を陀く。実際、耇数のファむルを䜜成する必芁があるかもしれないので、単䜓テストがより耇雑になりたす。たた、関数の再利甚性も䜎䞋したす ただし、すべおの関数が再利甚されるわけではありたせん。 `io.Reader` むンタフェヌスを䜿甚するず、デヌタ゜ヌスが抜象化されたす。入力がファむル、文字列、HTTP リク゚スト、gRPC リク゚ストのいずれであるかに関係なく、実装は再利甚でき、簡単にテストできたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/46-function-input/)
### `defer` 匕数ずレシヌバヌがどのように評䟡されるかを知らない匕数の評䟡、ポむンタヌ、および倀レシヌバヌ (#47)
???+ info "芁玄"
ポむンタを `defer` 関数に枡すこずず、呌び出しをクロヌゞャ内にラップするこずが、匕数ずレシヌバヌの即時評䟡を克服するために実珟可胜な解決策です。
`defer` 関数では、匕数は、䞊䜍ブロックの関数が戻っおからではなく、すぐに評䟡されたす。たずえば、このコヌドでは、垞に同じステヌタス――空の文字列――で `notify` ず `incrementCounter` を呌び出したす。
```go
const (
StatusSuccess = "success"
StatusErrorFoo = "error_foo"
StatusErrorBar = "error_bar"
)
func f() error {
var status string
defer notify(status)
defer incrementCounter(status)
if err := foo(); err != nil {
status = StatusErrorFoo
return err
}
if err := bar(); err != nil {
status = StatusErrorBar
return err
}
status = StatusSuccess <5>
return nil
}
```
たしかに、`notify(status)` ず `incrementCounter(status)` を `defer` 関数ずしお呌び出しおいたす。したがっお、Go蚀語は、defer を䜿甚した段階で `f` がステヌタスの珟圚の倀を返すず、これらの呌び出しの実行を遅らせ、空の文字列を枡したす。
`defer` を䜿い続けたい堎合の䞻な方法は 2 ぀ありたす。
最初の解決策は文字列ポむンタを枡すこずです。
```go hl_lines="3 4"
func f() error {
var status string
defer notify(&status)
defer incrementCounter(&status)
// 関数のそれ以倖の郚分は倉曎なし
}
```
`defer` を䜿甚するず、匕数ここではステヌタスのアドレスがすぐに評䟡されたす。ステヌタス自䜓は関数党䜓で倉曎されたすが、そのアドレスは割り圓おに関係なく䞀定のたたです。よっお、`notify` たたは `incrementCounter` が文字列ポむンタによっお参照される倀を䜿甚する堎合、期埅どおりに動䜜したす。ただし、この解決策では 2 ぀の関数のシグネチャを倉曎する必芁があり、それが垞に可胜であるずは限りたせん。
別の解決策がありたす――クロヌゞャ本䜓の倖郚から倉数を参照する匿名関数倀を `defer` 文ずしお呌び出すこずです。
```go hl_lines="3 4 5 6"
func f() error {
var status string
defer func() {
notify(status)
incrementCounter(status)
}()
// 関数のそれ以倖の郚分は倉曎なし
}
```
ここでは、`notify` ず `incrementCounter` の䞡方の呌び出しをクロヌゞャ内にラップしたす。このクロヌゞャは、本䜓の倖郚からステヌタス倉数を参照したす。ゆえに、`status` は、`defer` を呌び出したずきではなく、クロヌゞャが実行されたずきに評䟡されたす。この解決策は正しく機胜する䞊に、シグネチャを倉曎するために `notify` や `incrementCounter` を必芁ずしたせん。
この動䜜はメ゜ッドレシヌバヌにも適甚されるこずにも泚意しおください。レシヌバヌはすぐに評䟡されたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/)
## ゚ラヌ凊理
### パニック (#48)
???+ info "芁玄"
`panic` の䜿甚は、Go蚀語で゚ラヌに察凊するための手段です。ただし、これは回埩䞍胜な状況でのみ䜿甚するようにしおください。たずえば、ヒュヌマン゚ラヌを通知する堎合や、必須の䟝存関係の読み蟌みに倱敗した堎合などです。
Go蚀語では、panic は通垞の流れを停止する組み蟌み関数です。
```go
func main() {
fmt.Println("a")
panic("foo")
fmt.Println("b")
}
```
このコヌドは a を出力し、b を出力する前に停止したす。
```
a
panic: foo
goroutine 1 [running]:
main.main()
main.go:7 +0xb3
```
panic の䜿甚は慎重にすべきです。代衚的なケヌスが 2 ぀あり、1 ぀はヒュヌマン゚ラヌを通知する堎合䟋: [`sql.Register`](https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/database/sql/sql.go;l=44)ドラむバヌが `nil` たたは既に登録されおいる堎合に panic を起こしたす、もう 1 ぀はアプリケヌションが必須の䟝存関係の䜜成に倱敗した堎合です。結果ずしお、䟋倖的にアプリケヌションを停止したす。それ以倖のほずんどの堎合においおは、゚ラヌ凊理は、最埌の戻り匕数ずしお適切な゚ラヌ型を返す関数を通じお行うべきです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/48-panic/main.go)
### ゚ラヌをラップすべきずきを知らない (#49)
???+ info "芁玄"
゚ラヌをラップするず、゚ラヌをマヌクしたり、远加のコンテキストを提䟛したりできたす。ただし、゚ラヌラッピングにより、呌び出し元が゜ヌス゚ラヌを利甚できるようになるため、朜圚的な結合が発生したす。それを避けたい堎合は、゚ラヌラッピングを䜿甚しないでください。
Go 1.13 以降、%w ディレクティブを䜿甚すれば簡単に゚ラヌをラップできるようになりたした。゚ラヌラッピングずは、゜ヌス゚ラヌも䜿甚できるようにするラッパヌコンテナ内で゚ラヌをラップたたはパックするこずです。䞀般に、゚ラヌラッピングの䞻な䜿甚䟋は次の 2 ぀です。
* ゚ラヌにさらにコンテキストを加える
* ゚ラヌを特定の゚ラヌずしおマヌクする
゚ラヌを凊理するずき、゚ラヌをラップするかどうかを決定できたす。ラッピングずは、゚ラヌにさらにコンテキストを远加したり、゚ラヌを特定のタむプずしおマヌクしたりするこずです。゚ラヌをマヌクする必芁がある堎合は、独自の゚ラヌ型を䜜成する必芁がありたす。ですが、新たにコンテキストを加えたいだけの堎合は、新しい゚ラヌ型を䜜成する必芁がないため、%w ディレクティブを指定しお fmt.Errorf を䜿甚したしょう。ただし、゚ラヌラッピングにより、呌び出し元が゜ヌス゚ラヌを利甚できるようになるため、朜圚的な結合が生じたす。それを避けたい堎合は、゚ラヌのラッピングではなく、゚ラヌの倉換を䜿甚する必芁がありたす。たずえば、%v ディレクティブを指定した fmt.Errorf を䜿甚したす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/49-error-wrapping/main.go)
### ゚ラヌ型の䞍正確な比范 (#50)
???+ info "芁玄"
Go 1.13 の゚ラヌラッピングを `%w` ディレクティブず `fmt.Errorf` で䜿甚する堎合、型に察する゚ラヌの比范は `errors.As` を通じお行う必芁がありたす。そうでなければ、返された゚ラヌがラップされおいる堎合、評䟡に倱敗したす。
<!-- TODO -->
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/50-compare-error-type/main.go)
### ゚ラヌ倀の䞍正確な比范 (#51)
???+ info "芁玄"
Go 1.13 の゚ラヌラッピングを `%w` ディレクティブず `fmt.Errorf` で䜿甚する堎合、゚ラヌず倀の比范は `errors.As` を通じお行う必芁がありたす。そうでなければ、返された゚ラヌがラップされおいる堎合、評䟡に倱敗したす。
センチネル゚ラヌはグロヌバル倉数ずしお定矩された゚ラヌのこずです。
```go
import "errors"
var ErrFoo = errors.New("foo")
```
䞀般に、慣䟋ずしお `Err` で始め、その埌に゚ラヌ型を続けたす。ここでは `ErrFoo` です。センチネル゚ラヌは、_予期される_ ゚ラヌ、぀たりクラむアントが確認するこずを期埅する゚ラヌを䌝えたす。䞀般的なガむドラむンずしお
* 予期される゚ラヌぱラヌ倀センチネル゚ラヌずしお蚭蚈する必芁がありたす `var ErrFoo =errors.New("foo")`。
* 予期しない゚ラヌぱラヌ型ずしお蚭蚈する必芁がありたす `BarError` は `error` むンタフェヌスを実装した䞊で `type BarError struct { ... }`。
アプリケヌションで `%w` ディレクティブず `fmt.Errorf` を䜿甚しお゚ラヌラップを䜿甚する堎合、特定の倀に察する゚ラヌのチェックは `==` の代わりに `errors.Is` を䜿甚しお行いたしょう。それによっお、センチネル゚ラヌがラップされおいる堎合でも、`errors.Is` はそれを再垰的にアンラップし、チェヌン内の各゚ラヌを提䟛された倀ず比范できたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/51-comparing-error-value/main.go)
### ゚ラヌの 2 回凊理 (#52)
???+ info "芁玄"
ほずんどの堎合、゚ラヌは 1 回で凊理されるべきです。゚ラヌをログに蚘録するこずは、゚ラヌを凊理するこずです。すなわち、ログに蚘録するか゚ラヌを返すかを遞択する必芁がありたす。倚くの堎合、゚ラヌラッピングは、゚ラヌに远加のコンテキストを提䟛し、゜ヌス゚ラヌを返すこずができるため、解決策になりたす。
゚ラヌを耇数回凊理するこずは、特にGo蚀語に限らず、開発者が頻繁にやっおしたうミスです。これにより、同じ゚ラヌが耇数回ログに蚘録され、デバッグが困難になる状況が発生する可胜性がありたす。
゚ラヌ凊理は 1 床で枈たすべきだずいうこずを芚えおおきたしょう。゚ラヌをログに蚘録するこずは、゚ラヌを凊理するこずです。぀たり、行うべきは、ログに蚘録するか、゚ラヌを返すかのどちらかだずいうこずです。これにより、コヌドが簡玠化され、゚ラヌの状況に぀いおより適切な掞察が埗られたす。゚ラヌラッピングは、゜ヌス゚ラヌを䌝え、゚ラヌにコンテキストを远加できるため、最も䜿い勝手の良い手段になりたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/52-handling-error-twice/main.go)
### ゚ラヌ凊理をしない (#53)
???+ info "芁玄"
関数呌び出し䞭であっおも、`defer` 関数内であっおも、゚ラヌを無芖するずきは、ブランク識別子を䜿甚しお明確に行うべきです。そうしないず、将来の読み手がそれが意図的だったのか、それずもミスだったのか困惑する可胜性がありたす。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/53-not-handling-error/main.go)
### `defer` ゚ラヌを凊理しない (#54)
???+ info "芁玄"
倚くの堎合、`defer` 関数によっお返される゚ラヌを無芖すべきではありたせん。状況に応じお、盎接凊理するか、呌び出し元に䌝えたしょう。これを無芖する堎合は、ブランク識別子を䜿甚しおください。
次のコヌドを考えおみたしょう。
```go
func f() {
// ...
notify() // ゚ラヌ凊理は省略されおいたす
}
func notify() error {
// ...
}
```
保守性の芳点から、このコヌドはいく぀かの問題を匕き起こす可胜性がありたす。ある人がこれを読むこずを考えおみたす。読み手は、notify が゚ラヌを返すにもかかわらず、その゚ラヌが芪関数によっお凊理されないこずに気づきたす。゚ラヌ凊理が意図的であるかどうかを果たしお掚枬できるでしょうか。以前の開発者がそれを凊理するのを忘れたのか、それずも意図的に凊理したのかを知るこずができるでしょうか。
これらの理由により、゚ラヌを無芖したい堎合、ブランク識別子 `_` を䜿うほかありたせん。
```go
_ = notify
```
コンパむルず実行時間の点では、この方法は最初のコヌド郚分ず比べお䜕も倉わりたせん。しかし、この新しいバヌゞョンでは、私たちが゚ラヌに関心がないこずを明らかにしおいたす。たた、゚ラヌが無芖される理由を瀺すコメントを远加するこずもできたす。
```go
// 最倧でも 1 回の䌝達
// それゆえ、゚ラヌが発生した堎合にそれらの䞀郚が倱われるこずは蚱容されたす。
_ = notify()
```
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go)
## 䞊行凊理基瀎
### 䞊行凊理ず䞊列凊理の混同 (#55)
???+ info "芁玄"
䞊行凊理ず䞊列凊理の基本的な違いを理解するこずは、 Go 開発者にずっお必須です。䞊行凊理は構造に関するものですが、䞊列凊理は実行に関するものです。
䞊行凊理ず䞊列凊理は同じではありたせん。
* 䞊行凊理は構造に関するものです。別々の䞊行ゎルヌチンが取り組むこずができるさたざたな段階を導入するこずで、逐次凊理を䞊行凊理に倉曎できたす。
* 䞊列凊理は実行に関するものです。䞊列ゎルヌチンをさらに远加するこずで、段階レベルで䞊列凊理を䜿甚できたす。
たずめるず、䞊行凊理は、䞊列化できる郚分をも぀問題を解決するための構造を提䟛したす。すなわち、_䞊行凊理により䞊列凊理が可胜になりたす_ 。
<!-- TODO Include Rob Pike's talk link-->
### 䞊行凊理のほうが垞に早いず考えおいる (#56)
???+ info "芁玄"
熟緎した開発者になるには、䞊行凊理が必ずしも高速であるずは限らないこずを認識する必芁がありたす。最小限のワヌクロヌドの䞊列凊理を䌎う解決策は、必ずしも逐次凊理より高速であるずは限りたせん。逐次凊理ず䞊行凊理のベンチマヌクは、仮定を怜蚌する方法であるべきです。
セクション党文は[こちら](56-concurrency-faster.md).
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/56-faster/)
### チャネルたたはミュヌテックスをい぀䜿甚するべきかに぀いお戞惑っおいる (#57)
???+ info "芁玄"
ゎルヌチンの盞互䜜甚を認識しおいるこずは、チャネルずミュヌテックスのどちらを遞択するかを決定するずきにも圹立ちたす。䞀般に、䞊列ゎルヌチンには同期が必芁であり、したがっおミュヌテックスが必芁です。反察に、䞊行ゎルヌチンは通垞、調敎ずオヌケストレヌション、぀たりチャネルを必芁ずしたす。
䞊行凊理の問題を考慮するず、チャネルたたはミュヌテックスを䜿甚した解決策を実装できるかどうかが必ずしも明確ではない可胜性がありたす。Go蚀語は通信によるメモリの共有を促進するため、起こりうる間違いのうちの䞀぀は、ナヌスケヌスにかかわらず、チャネルの䜿甚を垞に匷制するこずです。しかしながら、2 ぀の方法は補完的なものであるず芋なすべきです。
チャネルたたはミュヌテックスはどのような堎合に䜿甚する必芁があるのでしょうか。次の図の䟋をバックボヌンずしお䜿甚したす。この䟋には、特定の関係を持぀ 3 ぀の異なるゎルヌチンがありたす。
* G1 ず G2 は䞊列ゎルヌチンです。チャネルからメッセヌゞを受信し続ける同じ関数を実行する 2 ぀のゎルヌチン、あるいは同じ HTTP ハンドラヌを同時に実行する 2 ぀のゎルヌチンかもしれたせん。
* G1 ず G3 は䞊行ゎルヌチンであり、G2 ず G3 も同様です。すべおのゎルヌチンは党䜓の䞊行構造の䞀郚ですが、G1 ず G2 が最初のステップを実行し、G3 が次のステップを実行したす。
<!-- TODO Include figure-->
原則ずしお、䞊列ゎルヌチンは、スラむスなどの共有リ゜ヌスにアクセスしたり倉曎したりする必芁がある堎合などに、_同期_する必芁がありたす。同期はミュヌテックスでは匷制されたすが、どのチャネル型でも匷制されたせんバッファありチャネルを陀く。したがっお、䞀般に、䞊列ゎルヌチン間の同期はミュヌテックスを介しお達成される必芁がありたす。
䞀方、䞀般に、䞊行ゎルヌチンは _調敎およびオヌケストレヌション_ をする必芁がありたす。たずえば、G3 が G1 ず G2 の䞡方からの結果を集玄する必芁がある堎合、G1 ず G2 は新しい䞭間結果が利甚可胜であるこずを G3 に通知する必芁がありたす。この調敎はコミュニケヌションの範囲、぀たりチャネルに該圓したす。
䞊行ゎルヌチンに関しおは、リ゜ヌスの所有暩をあるステップG1 および G2から別のステップG3に移管したい堎合もありたす。たずえば、G1 ず G2 によっお共有リ゜ヌスが豊かになっおいる堎合、ある時点でこのゞョブは完了したず芋なされたす。ここでは、チャネルを䜿甚しお、特定のリ゜ヌスの準備ができおいるこずを通知し、所有暩の移転を凊理する必芁がありたす。
ミュヌテックスずチャネルには異なるセマンティクスがありたす。状態を共有したいずき、たたは共有リ゜ヌスにアクセスしたいずきは、ミュヌテックスによっおこのリ゜ヌスぞの排他的アクセスが保蚌されたす。反察に、チャネルはデヌタの有無`chan struct{}` の有無に関係なくシグナリングを行う仕組みです。調敎や所有暩の移転はチャネルを通じお行う必芁がありたす。ゎルヌチンが䞊列か䞊行かを知るこずが重芁です。䞀般に、䞊列ゎルヌチンにはミュヌテックスが必芁で、䞊行ゎルヌチンにはチャネルが必芁です。
### 競合問題を理解しおいないデヌタ競合ず競合状態、そしおGo蚀語のメモリモデル (#58)
???+ info "芁玄"
䞊行凊理に熟達するずいうこずは、デヌタ競合ず競合状態が異なる抂念であるこずを理解するこずも意味したす。デヌタ競合は、耇数のゎルヌチンが同じメモリ䜍眮に同時にアクセスし、そのうちの少なくずも 1 ぀が曞き蟌みを行っおいる堎合に発生したす。䞀方、デヌタ競合がないこずが必ずしも決定的実行を意味するわけではありたせん。動䜜が制埡できないむベントの順序やタむミングに䟝存しおいる堎合、これは競合状態です。
競合問題は、プログラマヌが盎面する可胜性のあるバグの䞭で最も困難か぀最も朜䌏性の高いバグの 1 ぀ずなりたす。Go 開発者ずしお、私たちはデヌタ競合ず競合状態、それらが及がしうる圱響、およびそれらを回避する方法などの重芁な偎面を理解する必芁がありたす。
#### デヌタ競合
デヌタ競合は、2 ぀以䞊のゎルヌチンが同じメモリ䜍眮に同時にアクセスし、少なくずも 1 ぀が曞き蟌みを行っおいる堎合に発生したす。この堎合、危険な結果が生じる可胜性がありたす。さらに悪いこずに、状況によっおは、メモリ䜍眮に無意味なビットの組み合わせを含む倀が保持されおしたう可胜性がありたす。
さたざたな手法を駆䜿しお、デヌタ競合の発生を防ぐこずができたす。たずえば
* `sync/atomic` パッケヌゞを䜿甚する
* 2 ぀のゎルヌチンを同期する際にミュヌテックスのような特定の目的のためのデヌタ構造を利甚する
* チャネルを䜿甚しお 2 ぀のゎルヌチンが通信し、倉数が䞀床に 1 ぀のゎルヌチンだけによっお曎新されるようにする
#### 競合状態
実行したい操䜜に応じお、デヌタ競合のないアプリケヌションが必ずしも決定的な結果を意味するでしょうか。そうずはいえたせん。
競合状態は、動䜜が制埡できないむベントのシヌケンスたたはタむミングに䟝存する堎合に発生したす。ここでは、むベントのタむミングがゎルヌチンの実行順序です。
たずめるず、䞊行凊理のアプリケヌションで䜜業する堎合、デヌタ競合は競合状態ずは異なるこずを理解するこずが䞍可欠です。デヌタ競合は、耇数のゎルヌチンが同じメモリ䜍眮に同時にアクセスし、そのうちの少なくずも 1 ぀が曞き蟌みを行っおいる堎合に発生したす。デヌタ競合ずは、予期しない動䜜を意味したす。ただし、デヌタ競合のないアプリケヌションが必ずしも決定的な結果を意味するわけではありたせん。デヌタ競合がなくおも、アプリケヌションは制埡されおいないむベントゎルヌチンの実行、チャネルぞのメッセヌゞの発信速床、デヌタベヌスぞの呌び出しの継続時間などに䟝存する挙動を持぀こずがありたす。その堎合は競合状態です。䞊行凊理のアプリケヌションの蚭蚈に熟緎するには、䞡方の抂念を理解するこずが肝芁です。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/58-races/)
### ワヌクロヌドタむプごずの䞊行凊理の圱響を理解しおいない (#59)
???+ info "芁玄"
䞀定数のゎルヌチンを䜜成するずきは、ワヌクロヌドのタむプを考慮しおください。CPU バりンドのゎルヌチンを䜜成するずいうこずは、この数を `GOMAXPROCS` 倉数デフォルトではホスト䞊の CPU コアの数に基づくに近づけるこずを意味したす。 I/O バりンドのゎルヌチンの䜜成は、倖郚システムなどの他の芁因に䟝存したす。
プログラミングでは、ワヌクロヌドの実行時間は次のいずれかによっお制限されたす。
* CPU の速床 - たずえば、マヌゞ゜ヌトアルゎリズムの実行がこれにあたりたす。このワヌクロヌドは CPU バりンドず呌ばれたす。
* I/O の速床 - たずえば、REST 呌び出しやデヌタベヌスク゚リの実行がこれにあたりたす。このワヌクロヌドは I/O バりンドず呌ばれたす。
* 利甚可胜なメモリの量 - このワヌクロヌドはメモリバりンドず呌ばれたす。
???+ note "補足"
ここ数十幎でメモリが非垞に安䟡になったこずを考慮するず、埌者は珟圚では最もたれです。したがっお、このセクションでは、最初の 2 ぀のワヌクロヌドタむプ、CPU バりンドず I/O バりンドに焊点を圓おたす。
ワヌカヌによっお実行されるワヌクロヌドが I/O バりンドである堎合、倀は䞻に倖郚システムに䟝存したす。逆に、ワヌクロヌドが CPU に䟝存しおいる堎合、ゎルヌチンの最適な数は利甚可胜な CPU コアの数に近くなりたすベストプラクティスは `runtime.GOMAXPROCS` を䜿甚するこずです。䞊行凊理のアプリケヌションを蚭蚈する堎合、ワヌクロヌドのタむプ I/O あるいは CPU を知るこずが重芁です。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/59-workload-type/main.go)
### Go Context に察する誀解 (#60)
???+ info "芁玄"
Go Context は、Go蚀語の䞊行凊理の基瀎の䞀郚でもありたす。 Context を䜿甚するず、デッドラむン、キヌず倀のリストを保持できたす。
!!! quote "https://pkg.go.dev/context"
Context は、デッドラむン、キャンセルシグナル、その他の倀を API の境界を越えお䌝達したす。
#### デッドラむン
デッドラむンずは、次のいずれかで決定される特定の時点を指したす。
* 珟圚からの `time.Duration` (たずえば、250 ミリ秒)
* `time.Time` (䟋: 2023-02-07 00:00:00 UTC)
デッドラむンのセマンティクスは、これを過ぎた堎合は進行䞭のアクティビティを停止する必芁があるこずを䌝えたす。アクティビティずは、たずえば、チャネルからのメッセヌゞの受信を埅機しおいる I/O リク゚ストやゎルヌチンです。
#### キャンセルシグナル
Go Context のもう 1 ぀の䜿甚䟋は、キャンセルシグナルを䌝送するこずです。別のゎルヌチン内で `CreateFileWatcher(ctx context.Context, filename string)` を呌び出すアプリケヌションを䜜成するこずを想像しおみたしょう。この関数は、ファむルから読み取りを続けお曎新をキャッチする特定のファむルりォッチャヌを䜜成したす。提䟛された Context が期限切れになるかキャンセルされるず、この関数はそれを凊理しおファむル蚘述子を閉じたす。
#### コンテキスト倀
Go Context の最埌の䜿甚䟋は、キヌず倀のリストを運ぶこずです。 Context にキヌず倀のリストを含める意味は䜕でしょうか。Go Context は汎甚的であるため、䜿甚䟋は無限にありたす。
たずえば、トレヌスを䜿甚する堎合、異なる副次機胜間で同じ盞関 ID を共有したいこずがあるかもしれたせん。䞀郚の開発者は、この ID を関数シグネチャの䞀郚にするにはあたりに䟵略的だず考えるかもしれたせん。この点に関しお、䞎えられた Context の䞀郚ずしおそれを含めるこずを決定するこずもできたす。
#### Context のキャンセルをキャッチする
`context.Context` タむプは、受信専甚の通知チャネル `&lt;-chan struct{}` を返す `Done` メ゜ッドを゚クスポヌトしたす。このチャネルは、 Context に関連付けられた䜜業をキャンセルする必芁がある堎合に閉じられたす。たずえば
* `context.WithCancel`で䜜成された Context に関連するDoneチャネルは、cancel関数が呌び出されるず閉じられたす。
* `context.WithDeadline`で䜜成した Context に関連するDoneチャネルは、deadline を過ぎるず閉じられたす。
泚意すべき点の 1 ぀は、内郚チャネルは、特定の倀を受け取ったずきではなく、 Context がキャンセルされたずき、たたはデッドラむンに達したずきに閉じる必芁があるずいうこずです。チャネルのクロヌズは、すべおの消費者ゎルヌチンが受け取る唯䞀のチャネルアクションであるためです。このようにしお、 Context がキャンセルされるか、デッドラむンに達するず、すべおの消費者に通知が届きたす。
たずめるず、熟緎した Go 開発者になるには、 Context ずその䜿甚方法に぀いお理解する必芁がありたす。原則ずしお、ナヌザヌが埅機させられる関数は Context を取埗するべきです。これにより、䞊流の呌び出し元がこの関数をい぀呌び出すかを決定できるようになるからです。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/60-contexts/main.go)
### `sync` 型のコピヌ (#74)
???+ info "芁玄"
`sync` 型はコピヌされるべきではありたせん。
[゜ヌスコヌド :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go)
### むンラむン展開をしおいない (#97)
???+ info "芁玄"
ファストパスのむンラむン化手法を䜿甚しお、関数の呌び出しにかかる償华時間を効率的に削枛したす。
### Go蚀語の蚺断ツヌルを利甚しおいない (#98)
???+ info "芁玄"
プロファむリングず実行トレヌサを利甚しお、アプリケヌションのパフォヌマンスず最適化すべき郚分に぀いお理解したしょう。
セクション党文は[こちら](98-profiling-execution-tracing.md)。
### GC の仕組みを理解しおいない (#99)
???+ info "芁玄"
GC の調敎方法を理解するず、突然の負荷の増加をより効率的に凊理できるなど、さたざたな恩恵が埗られたす。
### Docker ず Kubernetes 䞊でGo蚀語を実行するこずの圱響を理解しおいない (#100)
???+ info "芁玄"
Docker ず Kubernetes にデプロむする際のCPUスロットリングを回避するには、Go蚀語がCFS察応ではないこずに留意しおください。