
こんにちは。よっしーです(^^)
本日は、Go言語を効果的に使うためのガイドラインについて解説しています。
背景
Go言語を学び始めて、より良いコードを書きたいと思い、Go言語の公式ドキュメント「Effective Go」を知りました。これは、いわば「Goらしいコードの書き方指南書」になります。単に動くコードではなく、効率的で保守性の高いコードを書くためのベストプラクティスが詰まっているので、これを読んだ時の内容を備忘として残しました。
Docコメント
「Docコメント」は、トップレベルのpackage、const、func、type、var宣言の直前に、改行を挟まずに現れるコメントです。エクスポートされた(大文字で始まる)すべての名前には、docコメントが必要です。
go/docとgo/doc/commentパッケージは、Goソースコードからドキュメントを抽出する機能を提供し、様々なツールがこの機能を利用しています。go doc
コマンドは、指定されたパッケージやシンボルのdocコメントを検索して表示します。(シンボルとは、トップレベルのconst、func、type、varのことです。)ウェブサーバー pkg.go.dev は、公開Goパッケージのドキュメントを表示します(ライセンスが許可する場合)。そのサイトを提供しているプログラムは golang.org/x/pkgsite/cmd/pkgsite で、プライベートモジュールのドキュメントを表示したり、インターネット接続なしで使用したりするために、ローカルで実行することもできます。言語サーバー gopls は、IDEでGoソースファイルを編集する際にドキュメントを提供します。
このページの残りの部分では、Go docコメントの書き方を説明しています。
型
型のdocコメントは、その型の各インスタンスが何を表すか、または何を提供するかを説明すべきです。APIがシンプルな場合、docコメントは非常に短くできます。例えば:
package zip
// A Reader serves content from a ZIP archive.
type Reader struct {
...
}
デフォルトでは、プログラマーは型が一度に単一のゴルーチンでのみ使用することが安全であると想定すべきです。型がより強い保証を提供する場合、docコメントでそれを記述すべきです。例えば:
package regexp
// Regexp is the representation of a compiled regular expression.
// A Regexp is safe for concurrent use by multiple goroutines,
// except for configuration methods, such as Longest.
type Regexp struct {
...
}
Goの型は、ゼロ値が有用な意味を持つことも目指すべきです。それが明白でない場合、その意味をドキュメント化すべきです。例えば:
package bytes
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
...
}
エクスポートされたフィールドを持つ構造体の場合、docコメントまたはフィールドごとのコメントで、各エクスポートされたフィールドの意味を説明すべきです。例えば、この型のdocコメントはフィールドを説明しています:
package io
// A LimitedReader reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
// Read returns EOF when N <= 0.
type LimitedReader struct {
R Reader // underlying reader
N int64 // max bytes remaining
}
対照的に、この型のdocコメントは説明をフィールドごとのコメントに委ねています:
package comment
// A Printer is a doc comment printer.
// The fields in the struct can be filled in before calling
// any of the printing methods
// in order to customize the details of the printing process.
type Printer struct {
// HeadingLevel is the nesting level used for
// HTML and Markdown headings.
// If HeadingLevel is zero, it defaults to level 3,
// meaning to use <h3> and ###.
HeadingLevel int
...
}
パッケージ(上記)や関数(以下)と同様に、型のdocコメントは宣言されたシンボルを名付ける完全な文で始まります。明示的な主語は、しばしば文言をより明確にし、ウェブページでもコマンドラインでも、テキストを検索しやすくします。例えば:
$ go doc -all regexp | grep pairs
pairs within the input string: result[2*n:2*n+2] identifies the indexes
FindReaderSubmatchIndex returns a slice holding the index pairs identifying
FindStringSubmatchIndex returns a slice holding the index pairs identifying
FindSubmatchIndex returns a slice holding the index pairs identifying the
$
重要ポイント
- 型の説明の基本
- 型のインスタンスが何を表すか
- どのような機能を提供するか
- 完全な文で型名から開始
- 並行性の安全性
- デフォルトは単一ゴルーチンでの使用
- 並行安全性がある場合は明示的に記述
- 例外や制限事項も明記
- ゼロ値の意味
- ゼロ値が有用であることを説明
- 初期化不要で使える場合は明記
- フィールドの説明方法
- 型のdocコメントで説明
- フィールドごとにコメント
- 両方の組み合わせ
良い型ドキュメントの例
// Client represents an HTTP client.
// Client is safe for concurrent use by multiple goroutines.
// The zero value for Client is a usable client that uses DefaultTransport.
type Client struct {
// Transport specifies the mechanism by which individual
// HTTP requests are made. If nil, DefaultTransport is used.
Transport RoundTripper
// Timeout specifies a time limit for requests made by this Client.
// A Timeout of zero means no timeout.
Timeout time.Duration
}
// Config holds the configuration for the application.
// All fields are optional and have sensible defaults.
type Config struct {
// Port specifies the server port. Defaults to 8080.
Port int
// Workers controls the number of worker goroutines.
// If zero, defaults to runtime.NumCPU().
Workers int
}
検索可能性の重要性
明示的な主語(型名)を使うことで:
go doc
での検索が容易- ドキュメントサイトでの検索性向上
- コードレビュー時の参照が明確
型のドキュメントは、APIの使用方法を理解する上で重要な役割を果たすため、明確で検索可能な形式で書くことが推奨されます。
おわりに
本日は、Go言語を効果的に使うためのガイドラインについて解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント