Go言語入門:Goドキュメント -Funcs-

スポンサーリンク
Go言語入門:Goドキュメント -Funcs- 用語解説
Go言語入門:Goドキュメント -Funcs-
この記事は約11分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、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コメントは、関数が何を返すか、または副作用のために呼び出される関数の場合は何を行うかを説明すべきです。名前付きパラメータと結果は、バッククォートのような特別な構文なしに、コメント内で直接参照できます。(この慣習の結果として、普通の単語と間違えられる可能性があるaのような名前は、通常避けられます。)例えば:

package strconv

// Quote returns a double-quoted Go string literal representing s.
// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
// for control characters and non-printable characters as defined by IsPrint.
func Quote(s string) string {
    ...
}

そして:

package os

// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
// The program terminates immediately; deferred functions are not run.
//
// For portability, the status code should be in the range [0, 125].
func Exit(code int) {
    ...
}

docコメントは通常、ブール値を返す関数を説明するために「reports whether」というフレーズを使用します。「or not」というフレーズは不要です。例えば:

package strings

// HasPrefix reports whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool

docコメントが複数の結果を説明する必要がある場合、結果に名前を付けると、関数本体で名前が使用されていなくても、docコメントをより理解しやすくできます。例えば:

package io

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs. It returns the total number of bytes
// written and the first error encountered while copying, if any.
//
// A successful Copy returns err == nil, not err == EOF.
// Because Copy is defined to read from src until EOF, it does
// not treat an EOF from Read as an error to be reported.
func Copy(dst Writer, src Reader) (n int64, err error) {
    ...
}

逆に、docコメントで結果に名前を付ける必要がない場合、表示を乱雑にしないために、上記のQuoteの例のように、コードでも通常省略されます。

これらのルールはすべて、通常の関数とメソッドの両方に適用されます。メソッドの場合、同じレシーバー名を使用することで、型のすべてのメソッドをリストする際の不必要な変化を避けます:

$ go doc bytes.Buffer
package bytes // import "bytes"

type Buffer struct {
    // Has unexported fields.
}
    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.

func NewBuffer(buf []byte) *Buffer
func NewBufferString(s string) *Buffer
func (b *Buffer) Bytes() []byte
func (b *Buffer) Cap() int
func (b *Buffer) Grow(n int)
func (b *Buffer) Len() int
func (b *Buffer) Next(n int) []byte
func (b *Buffer) Read(p []byte) (n int, err error)
func (b *Buffer) ReadByte() (byte, error)
...

この例は、型Tまたはポインタ*Tを返すトップレベル関数(おそらく追加のエラー結果付き)が、Tのコンストラクタであるという仮定の下で、型Tとそのメソッドと共に表示されることも示しています。

デフォルトでは、プログラマーはトップレベル関数が複数のゴルーチンから安全に呼び出せると想定できます。この事実は明示的に記述する必要はありません。

一方、前のセクションで述べたように、メソッドの呼び出しを含む型のインスタンスの使用は、通常、一度に単一のゴルーチンに制限されると想定されます。並行使用に安全なメソッドが型のdocコメントで文書化されていない場合、メソッドごとのコメントで文書化すべきです。例えば:

package sql

// Close returns the connection to the connection pool.
// All operations after a Close will return with ErrConnDone.
// Close is safe to call concurrently with other operations and will
// block until all other operations finish. It may be useful to first
// cancel any used context and then call Close directly after.
func (c *Conn) Close() error {
    ...
}

関数とメソッドのdocコメントは、操作が何を返すか、または何を行うかに焦点を当て、呼び出し側が知る必要があることを詳述することに注意してください。特殊なケースは特に文書化することが重要です。例えば:

package math

// Sqrt returns the square root of x.
//
// Special cases are:
//
//  Sqrt(+Inf) = +Inf
//  Sqrt(±0) = ±0
//  Sqrt(x < 0) = NaN
//  Sqrt(NaN) = NaN
func Sqrt(x float64) float64 {
    ...
}

docコメントは、現在の実装で使用されているアルゴリズムなどの内部詳細を説明すべきではありません。それらは関数本体内のコメントに任せるのが最適です。その詳細が呼び出し側にとって特に重要な場合、漸近的な時間または空間の境界を示すことが適切かもしれません。例えば:

package sort

// Sort sorts data in ascending order as determined by the Less method.
// It makes one call to data.Len to determine n and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
    ...
}

このdocコメントはどのソートアルゴリズムが使用されているかについて言及していないため、将来、実装を異なるアルゴリズムを使用するように変更することが容易です。

重要ポイント

  1. 関数docコメントの基本
    • 何を返すか、何をするかを説明
    • パラメータと戻り値は直接参照可能
    • 特別な構文は不要
  2. ブール値を返す関数
    • 「reports whether」を使用
    • 「or not」は不要
  3. 複数の戻り値
    • 名前付き戻り値で理解しやすく
    • コメントでの説明が不要なら省略可能
  4. 並行性の安全性
    • トップレベル関数:デフォルトで並行安全
    • メソッド:デフォルトで単一ゴルーチン
    • 例外は明示的に文書化
  5. 特殊ケースの文書化
    • エッジケースは重要
    • 明確に列挙
  6. 実装詳細の扱い
    • アルゴリズムの詳細は本文内で
    • 計算量は必要に応じて記載

良い関数ドキュメントの例

// ParseInt interprets a string s in the given base (0, 2 to 36) and
// bit size (0 to 64) and returns the corresponding value i.
//
// The string may begin with a leading sign: "+" or "-".
//
// If the base argument is 0, the true base is implied by the string's
// prefix following the sign (if present): 2 for "0b", 8 for "0" or "0o",
// 16 for "0x", and 10 otherwise. Also, for argument base 0 only,
// underscore characters are permitted as defined by the Go syntax for
// integer literals.
//
// The bitSize argument specifies the integer type that the result must fit
// into. Bit sizes 0, 8, 16, 32, and 64 correspond to int, int8, int16,
// int32, and int64. If bitSize is below 0 or above 64, an error is returned.
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s. If s is empty or contains invalid
// digits, err.Err = ErrSyntax and the returned value is 0;
// if the value corresponding to s cannot be represented by a
// signed integer of the given size, err.Err = ErrRange and the
// returned value is the maximum magnitude integer of the
// appropriate bitSize and sign.
func ParseInt(s string, base int, bitSize int) (i int64, err error)

おわりに 

本日は、Go言語を効果的に使うためのガイドラインについて解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました