Go ドキュメントコメント

「ドキュメントコメント」とは、トップレベルの package、const、func、type、および var 宣言の直前に、間に改行を入れずに記述されるコメントです。すべてのエクスポートされた(大文字で始まる)名前には、ドキュメントコメントが必要です。

go/doc パッケージと go/doc/comment パッケージは、Go ソースコードからドキュメントを抽出する機能を提供し、さまざまなツールがこの機能を利用しています。go doc コマンドは、指定されたパッケージまたはシンボルのドキュメントコメントを検索して表示します。(シンボルとは、トップレベルの const、func、type、または var です。)Web サーバー pkg.go.dev は、公開されている Go パッケージのドキュメントを表示します(ライセンスでその使用が許可されている場合)。このサイトを提供しているプログラムは golang.org/x/pkgsite/cmd/pkgsite で、プライベートモジュールやインターネットに接続せずにドキュメントを表示するためにローカルで実行することもできます。言語サーバー gopls は、IDE で Go ソースファイルを編集する際にドキュメントを提供します。

このページの残りの部分では、Go ドキュメントコメントの書き方について説明します。

パッケージ

すべてのパッケージには、パッケージを紹介するパッケージコメントが必要です。これは、パッケージ全体に関連する情報を提供し、一般的にパッケージに対する期待を設定します。特に大きなパッケージでは、パッケージコメントで API の最も重要な部分の概要を簡単に説明し、必要に応じて他のドキュメントコメントにリンクすると便利です。

パッケージが単純な場合、パッケージコメントは簡潔にすることができます。例えば

// Package path implements utility routines for manipulating slash-separated
// paths.
//
// The path package should only be used for paths separated by forward
// slashes, such as the paths in URLs. This package does not deal with
// Windows paths with drive letters or backslashes; to manipulate
// operating system paths, use the [path/filepath] package.
package path

[path/filepath] の角括弧は、ドキュメントリンクを作成します。

この例に見られるように、Go ドキュメントコメントは完全な文を使用します。パッケージコメントの場合、それは 最初の文 が「Package」で始まることを意味します」。

複数ファイルのパッケージの場合、パッケージコメントは1つのソースファイルにのみ記述する必要があります。複数ファイルにパッケージコメントがある場合、それらは連結されてパッケージ全体の1つの大きなコメントになります。

コマンド

コマンドのパッケージコメントも同様ですが、パッケージ内の Go シンボルではなく、プログラムの動作を記述します。最初の文は、慣例的にプログラムの名前で始まり、文頭に来るため大文字で始まります。たとえば、gofmt のパッケージコメントの要約版を以下に示します。

/*
Gofmt formats Go programs.
It uses tabs for indentation and blanks for alignment.
Alignment assumes that an editor is using a fixed-width font.

Without an explicit path, it processes the standard input. Given a file,
it operates on that file; given a directory, it operates on all .go files in
that directory, recursively. (Files starting with a period are ignored.)
By default, gofmt prints the reformatted sources to standard output.

Usage:

    gofmt [flags] [path ...]

The flags are:

    -d
        Do not print reformatted sources to standard output.
        If a file's formatting is different than gofmt's, print diffs
        to standard output.
    -w
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, overwrite it
        with gofmt's version. If an error occurred during overwriting,
        the original file is restored from an automatic backup.

When gofmt reads from standard input, it accepts either a full Go program
or a program fragment. A program fragment must be a syntactically
valid declaration list, statement list, or expression. When formatting
such a fragment, gofmt preserves leading indentation as well as leading
and trailing spaces, so that individual sections of a Go program can be
formatted by piping them through gofmt.
*/
package main

コメントの冒頭は、セマンティック改行を使用して書かれており、新しい文や長い句がそれぞれ独立した行に配置されています。これにより、コードとコメントが進化するにつれて差分を読みやすくなります。後の段落はこの規則に従っておらず、手動で折り返されています。コードベースにとって最適な方法で構いません。いずれにしても、go docpkgsite は、ドキュメントコメントのテキストを出力する際に、テキストを再ラップします。例えば

$ go doc gofmt
Gofmt formats Go programs. It uses tabs for indentation and blanks for
alignment. Alignment assumes that an editor is using a fixed-width font.

Without an explicit path, it processes the standard input. Given a file, it
operates on that file; given a directory, it operates on all .go files in that
directory, recursively. (Files starting with a period are ignored.) By default,
gofmt prints the reformatted sources to standard output.

Usage:

    gofmt [flags] [path ...]

The flags are:

    -d
        Do not print reformatted sources to standard output.
        If a file's formatting is different than gofmt's, print diffs
        to standard output.
...

インデントされた行は整形済みテキストとして扱われます。つまり、再ラップされず、HTML および Markdown プレゼンテーションではコードフォントで表示されます。(詳細は以下の構文セクションを参照してください。)

型のドキュメントコメントでは、その型の各インスタンスが何を表すか、または何を提供するかを説明する必要があります。API が単純な場合、ドキュメントコメントは非常に短くても構いません。例えば

package zip

// A Reader serves content from a ZIP archive.
type Reader struct {
    ...
}

デフォルトでは、プログラマは、型が一度に1つのゴルーチンによってのみ安全に使用できると期待する必要があります。型がより強力な保証を提供する場合、ドキュメントコメントでそれを明記する必要があります。例えば

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 {
    ...
}

エクスポートされたフィールドを持つ構造体の場合、ドキュメントコメントまたはフィールドごとのコメントで、各エクスポートされたフィールドの意味を説明する必要があります。たとえば、この型のドキュメントコメントは、フィールドを説明しています

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
}

対照的に、この型のドキュメントコメントは、説明をフィールドごとのコメントに任せています

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
    ...
}

パッケージ(上記)と関数(下記)と同様に、型のドキュメントコメントは、宣言されたシンボルを命名する完全な文で始まります。明示的な主語は、多くの場合、表現をより明確にし、Web ページやコマンドラインでの検索を容易にします。例えば

$ 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
$

関数

関数のドキュメントコメントでは、関数が何を返すか、または副作用のために呼び出される関数については、何をするかを説明する必要があります。名前付きパラメータと結果は、バッククォートなどの特別な構文を使用せずに、コメント内で直接参照できます。(この規則の結果として、通常の単語と間違えられる可能性のある `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) {
    ...
}

ドキュメントコメントでは、通常、「~かどうかを報告する」というフレーズを使用して、ブール値を返す関数を記述します。「かどうか」というフレーズは不要です。例えば

package strings

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

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

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) {
    ...
}

逆に、ドキュメントコメントで結果に名前を付ける必要がない場合は、上記の `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` とそのメソッドと一緒に表示されることを示しています。

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

一方、前のセクションで述べたように、メソッドの呼び出しを含む、型のインスタンスを何らかの方法で使用することは、通常、一度に1つのゴルーチンに制限されると想定されています。同時使用しても安全なメソッドが型のドキュメントコメントに記載されていない場合は、メソッドごとのコメントに記載する必要があります。例えば

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 {
    ...
}

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

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 {
    ...
}

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

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) {
    ...
}

このドキュメントコメントでは、どのソートアルゴリズムが使用されているかについては触れていないため、将来的に実装を変更して別のアルゴリズムを使用することが容易になります。

定数

Go の宣言構文では、宣言のグループ化が可能です。この場合、1つのドキュメントコメントで、関連する定数のグループを紹介し、個々の定数は行末の短いコメントによってのみ文書化できます。例えば

package scanner // import "text/scanner"

// The result of Scan is one of these tokens or a Unicode character.
const (
    EOF = -(iota + 1)
    Ident
    Int
    Float
    Char
    ...
)

グループにドキュメントコメントがまったく必要ない場合もあります。例えば

package unicode // import "unicode"

const (
    MaxRune         = '\U0010FFFF' // maximum valid Unicode code point.
    ReplacementChar = '\uFFFD'     // represents invalid code points.
    MaxASCII        = '\u007F'     // maximum ASCII value.
    MaxLatin1       = '\u00FF'     // maximum Latin-1 value.
)

一方、グループ化されていない定数は、通常、完全な文で始まる完全なドキュメントコメントが必要です。例えば

package unicode

// Version is the Unicode edition from which the tables are derived.
const Version = "13.0.0"

型付き定数は、型の宣言の横に表示されるため、型のドキュメントコメントを優先して、定数グループのドキュメントコメントを省略することがよくあります。例えば

package syntax

// An Op is a single regular expression operator.
type Op uint8

const (
    OpNoMatch        Op = 1 + iota // matches no strings
    OpEmptyMatch                   // matches empty string
    OpLiteral                      // matches Runes sequence
    OpCharClass                    // matches Runes interpreted as range pair list
    OpAnyCharNotNL                 // matches any character except newline
    ...
)

(HTML プレゼンテーションについては、pkg.go.dev/regexp/syntax#Op を参照してください。)

変数

変数の規則は定数の規則と同じです。たとえば、グループ化された変数のセットを次に示します

package fs

// Generic file system errors.
// Errors returned by file systems can be tested against these errors
// using errors.Is.
var (
    ErrInvalid    = errInvalid()    // "invalid argument"
    ErrPermission = errPermission() // "permission denied"
    ErrExist      = errExist()      // "file already exists"
    ErrNotExist   = errNotExist()   // "file does not exist"
    ErrClosed     = errClosed()     // "file already closed"
)

そして、単一の変数

package unicode

// Scripts is the set of Unicode script tables.
var Scripts = map[string]*RangeTable{
    "Adlam":                  Adlam,
    "Ahom":                   Ahom,
    "Anatolian_Hieroglyphs":  Anatolian_Hieroglyphs,
    "Arabic":                 Arabic,
    "Armenian":               Armenian,
    ...
}

構文

Go のドキュメントコメントは、段落、見出し、リンク、リスト、および整形済みコードブロックをサポートするシンプルな構文で記述されます。コメントを軽量にし、ソースファイルで読みやすくするために、フォントの変更や生の HTML などの複雑な機能はサポートされていません。Markdown に精通している人は、この構文を Markdown の簡略化されたサブセットと見なすことができます。

標準フォーマッタ gofmt は、ドキュメントコメントを再フォーマットして、これらの各機能に標準的なフォーマットを使用します。Gofmt は、ソースコードでコメントがどのように記述されるかを読みやすくし、ユーザーが制御できるようにすることを目指していますが、通常のソースコードで `1+2 * 3` を `1 + 2*3` に再フォーマットするのと同様に、特定のコメントの意味的な意味を明確にするためにプレゼンテーションを調整します。

`//go:generate` などのディレクティブコメントは、ドキュメントコメントの一部とは見なされず、レンダリングされたドキュメントからは省略されます。Gofmt は、ディレクティブコメントをドキュメントコメントの最後に移動し、その前に空行を挿入します。例えば

package regexp

// An Op is a single regular expression operator.
//
//go:generate stringer -type Op -trimprefix Op
type Op uint8

ディレクティブコメントとは、正規表現 `//(line |extern |export |[a-z0-9]+:[a-z0-9])` に一致する行です。独自のディレクティブを定義するツールは、`//toolname:directive` の形式を使用する必要があります。

Gofmt は、ドキュメントコメントの先頭と末尾の空白行を削除します。ドキュメントコメントのすべての行が同じスペースとタブのシーケンスで始まる場合、gofmt はそのプレフィックスを削除します。

段落

段落とは、インデントされていない空白行でない行の範囲です。段落の例はすでにたくさん見てきました。

連続する2つのバッククォート(` U+0060)は、Unicode の左引用符(“ U+201C)として解釈され、連続する2つのシングルクォート(' U+0027)は、Unicode の右引用符(” U+201D)として解釈されます。

Gofmt は、段落テキストの改行を保持します。つまり、テキストを再ラップしません。これにより、前述のように、セマンティック改行を使用できます。Gofmt は、段落間の重複した空白行を1つの空白行に置き換えます。Gofmt はまた、連続するバッククォートまたはシングルクォートを Unicode の解釈に再フォーマットします。

見出し

見出しは、番号記号(U+0023)で始まり、その後にスペースと見出しテキストが続く行です。見出しとして認識されるためには、行はインデントされておらず、隣接する段落テキストとは空行で区切られている必要があります。

例えば

// Package strconv implements conversions to and from string representations
// of basic data types.
//
// # Numeric Conversions
//
// The most common numeric conversions are [Atoi] (string to int) and [Itoa] (int to string).
...
package strconv

一方

// #This is not a heading, because there is no space.
//
// # This is not a heading,
// # because it is multiple lines.
//
// # This is not a heading,
// because it is also multiple lines.
//
// The next paragraph is not a heading, because there is no additional text:
//
// #
//
// In the middle of a span of non-blank lines,
// # this is not a heading either.
//
//     # This is not a heading, because it is indented.

# 構文は Go 1.19 で追加されました。Go 1.19 より前では、見出しは特定の条件を満たす単一行の段落、特に終端記号がないことによって暗黙的に識別されていました。

Gofmt は、以前のバージョンの Go で暗黙的な見出しとして扱われていたを再フォーマットし、代わりに # 見出しを使用します。再フォーマットが適切でない場合、つまり、行が見出しとして意図されていない場合は、ピリオドやコロンなどの終端記号を導入するか、2行に分割することで、段落にすることができます。

インデントされていない空でない行の範囲は、すべての行が「[テキスト]: URL」の形式である場合、リンクターゲットを定義します。同じドキュメントコメント内の他のテキストでは、「[テキスト]」は、指定されたテキストを使用して URL へのリンクを表します。HTML では、<a href=“URL”>テキスト</a> です。例えば

// Package json implements encoding and decoding of JSON as defined in
// [RFC 7159]. The mapping between JSON and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
//
// For an introduction to this package, see the article
// “[JSON and Go].”
//
// [RFC 7159]: https://tools.ietf.org/html/rfc7159
// [JSON and Go]: https://go.dokyumento.jp/doc/articles/json_and_go.html
package json

URL を別のセクションに保持することで、このフォーマットは実際のテキストの流れを最小限に妨げるだけです。また、オプションのタイトルテキストがないことを除けば、Markdown のショートカット参照リンク形式とほぼ一致します。

対応する URL 宣言がない場合、(次のセクションで説明するドキュメントリンクを除いて)「[テキスト]」はハイパーリンクではなく、表示される際に角括弧は保持されます。各ドキュメントコメントは独立して考慮されます。あるコメント内のリンクターゲット定義は、他のコメントには影響しません。

リンクターゲット定義ブロックは通常の段落とインターリーブできますが、gofmt はすべてのリンクターゲット定義をドキュメントコメントの最後に最大2つのブロックで移動します。最初にコメント内で参照されているすべてのリンクターゲットを含むブロック、次にコメント内で参照されていないすべてのターゲットを含むブロックです。個別のブロックにより、未使用のターゲットに気づきやすく、修正(リンクまたは定義に誤字がある場合)または削除(定義が不要になった場合)しやすくなります。

URL として認識されるプレーンテキストは、HTML レンダリングで自動的にリンクされます。

ドキュメントリンクは、「[Name1]」または「[Name1.Name2]」の形式のリンクで、現在の`package`内のエクスポートされた識別子を参照するか、「[pkg]」、「[pkg.Name1]」、または「[pkg.Name1.Name2]」の形式で他のパッケージ内の識別子を参照します。

例えば

package bytes

// ReadFrom reads data from r until EOF and appends it to the buffer, growing
// the buffer as needed. The return value n is the number of bytes read. Any
// error except [io.EOF] encountered during the read is also returned. If the
// buffer becomes too large, ReadFrom will panic with [ErrTooLarge].
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
    ...
}

シンボルリンクの括弧で囲まれたテキストには、オプションの先頭の星を含めることができ、[*bytes.Buffer] のようにポインター型を参照しやすくなります。

他のパッケージを参照する場合、「pkg」は完全なインポートパス、または既存のインポートの想定パッケージ名のいずれかになります。想定パッケージ名は、名前変更されたインポートの識別子、またはgoimports によって想定される名前のいずれかです。(goimports は、その想定が正しくない場合に名前変更を挿入するため、このルールは基本的にすべての Go コードで機能するはずです。)例えば、現在のパッケージが encoding/json をインポートする場合、encoding/json の Decoder のドキュメントにリンクするために、「[encoding/json.Decoder]」の代わりに「[json.Decoder]」と記述できます。パッケージ内の異なるソースファイルが同じ名前を使用して異なるパッケージをインポートする場合、省略形はあいまいであり、使用できません。

「pkg」は、ドメイン名(ドットを含むパス要素)で始まるか、標準ライブラリのパッケージの1つ(「[os]」、「[encoding/json]」など)である場合にのみ、完全なインポートパスと見なされます。例えば、`[os.File]` と `[example.com/sys.File]` はドキュメントリンクですが(後者は壊れたリンクになります)、標準ライブラリに os/sys パッケージがないため、`[os/sys.File]` はドキュメントリンクではありません。

マップ、ジェネリクス、および配列型の問題を回避するため、ドキュメントリンクの前後には句読点、スペース、タブ、または行の先頭または末尾が必要です。例えば、テキスト「map[ast.Expr]TypeAndValue」にはドキュメントリンクが含まれていません。

リスト

リストは、インデントされた行または空行(それ以外の場合は、次のセクションで説明するようにコードブロックになります)の範囲であり、最初のインデントされた行は箇条書きマーカーまたは番号付きリストマーカーで始まります。

箇条書きマーカーは、アスタリスク、プラス、ダッシュ、または Unicode の箇条書き(*、+、-、•; U+002A、U+002B、U+002D、U+2022)の後にスペースまたはタブ、そしてテキストが続きます。箇条書きでは、箇条書きマーカーで始まる各行が新しいリスト項目を開始します。

例えば

package url

// PublicSuffixList provides the public suffix of a domain. For example:
//   - the public suffix of "example.com" is "com",
//   - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
//   - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us".
//
// Implementations of PublicSuffixList must be safe for concurrent use by
// multiple goroutines.
//
// An implementation that always returns "" is valid and may be useful for
// testing but it is not secure: it means that the HTTP server for foo.com can
// set a cookie for bar.com.
//
// A public suffix list implementation is in the package
// golang.org/x/net/publicsuffix.
type PublicSuffixList interface {
    ...
}

番号付きリストマーカーは、任意の長さの10進数の後にピリオドまたは右括弧、次にスペースまたはタブ、そしてテキストが続きます。番号付きリストでは、番号付きリストマーカーで始まる各行が新しいリスト項目を開始します。項目番号はそのまま残され、番号が付け直されることはありません。

例えば

package path

// Clean returns the shortest path name equivalent to path
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
//
//  1. Replace multiple slashes with a single slash.
//  2. Eliminate each . path name element (the current directory).
//  3. Eliminate each inner .. path name element (the parent directory)
//     along with the non-.. element that precedes it.
//  4. Eliminate .. elements that begin a rooted path:
//     that is, replace "/.." by "/" at the beginning of a path.
//
// The returned path ends in a slash only if it is the root "/".
//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
// See also Rob Pike, “[Lexical File Names in Plan 9].”
//
// [Lexical File Names in Plan 9]: https://9p.io/sys/doc/lexnames.html
func Clean(path string) string {
    ...
}

リスト項目には段落のみが含まれ、コードブロックやネストされたリストは含まれません。これにより、スペースカウントの微妙さや、不整合なインデントでタブがいくつのスペースをカウントするかという疑問が回避されます。

Gofmt は箇条書きを再フォーマットし、箇条書きマーカーとしてダッシュ、ダッシュの前に2つのスペースのインデント、継続行に4つのスペースのインデントを使用します。

Gofmt は番号付きリストを再フォーマットし、番号の前に1つのスペース、番号の後にピリオド、継続行に4つのスペースのインデントを使用します。

Gofmt は、リストと前の段落の間の空行を保持しますが、必須ではありません。リストと次の段落または見出しの間に空行を挿入します。

コードブロック

コードブロックは、箇条書きマーカーまたは番号付きリストマーカーで始まらない、インデントされた行または空行の範囲です。プリフォーマットされたテキスト(HTML の <pre> ブロック)としてレンダリングされます。

コードブロックには、多くの場合、Go コードが含まれています。例えば

package sort

// Search uses binary search...
//
// As a more whimsical example, this program guesses your number:
//
//  func GuessingGame() {
//      var s string
//      fmt.Printf("Pick an integer from 0 to 100.\n")
//      answer := sort.Search(100, func(i int) bool {
//          fmt.Printf("Is your number <= %d? ", i)
//          fmt.Scanf("%s", &s)
//          return s != "" && s[0] == 'y'
//      })
//      fmt.Printf("Your number is %d.\n", answer)
//  }
func Search(n int, f func(int) bool) int {
    ...
}

もちろん、コードブロックにはコード以外のプリフォーマットされたテキストも含まれることがよくあります。例えば

package path

// Match reports whether name matches the shell pattern.
// The pattern syntax is:
//
//  pattern:
//      { term }
//  term:
//      '*'         matches any sequence of non-/ characters
//      '?'         matches any single non-/ character
//      '[' [ '^' ] { character-range } ']'
//                  character class (must be non-empty)
//      c           matches character c (c != '*', '?', '\\', '[')
//      '\\' c      matches character c
//
//  character-range:
//      c           matches character c (c != '\\', '-', ']')
//      '\\' c      matches character c
//      lo '-' hi   matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
// The only possible returned error is [ErrBadPattern], when pattern
// is malformed.
func Match(pattern, name string) (matched bool, err error) {
    ...
}

Gofmt は、コードブロック内のすべての行を1つのタブでインデントし、空でない行に共通する他のインデントを置き換えます。Gofmt は、各コードブロックの前後に空行を挿入し、コードブロックを周囲の段落テキストと明確に区別します。

よくある間違いと落とし穴

ドキュメントコメント内のインデントされた行または空行の範囲はコードブロックとしてレンダリングされるというルールは、Go の初期の頃から存在します。残念ながら、gofmt でのドキュメントコメントのサポートの欠如により、コードブロックを作成することを意図せずにインデントを使用する既存のコメントが多数存在します。

例えば、このインデントされていないリストは、godoc によって常に3行の段落とそれに続く1行のコードブロックとして解釈されてきました。

package http

// cancelTimerBody is an io.ReadCloser that wraps rc with two features:
// 1) On Read error or close, the stop func is called.
// 2) On Read failure, if reqDidTimeout is true, the error is wrapped and
//    marked as net.Error that hit its timeout.
type cancelTimerBody struct {
    ...
}

これは、常に `go` `doc` で次のようにレンダリングされます。

cancelTimerBody is an io.ReadCloser that wraps rc with two features:
1) On Read error or close, the stop func is called. 2) On Read failure,
if reqDidTimeout is true, the error is wrapped and

    marked as net.Error that hit its timeout.

同様に、このコメントのコマンドは、1行の段落とそれに続く1行のコードブロックです。

package smtp

// localhostCert is a PEM-encoded TLS cert generated from src/crypto/tls:
//
// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \
//     --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`...`)

これは `go` `doc` で次のようにレンダリングされます。

localhostCert is a PEM-encoded TLS cert generated from src/crypto/tls:

go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \

    --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h

そして、このコメントは2行の段落(2行目は「{」)、それに続く6行のインデントされたコードブロックと1行の段落(「}」)です。

// On the wire, the JSON will look something like this:
// {
//  "kind":"MyAPIObject",
//  "apiVersion":"v1",
//  "myPlugin": {
//      "kind":"PluginA",
//      "aOption":"foo",
//  },
// }

そして、これは `go` `doc` で次のようにレンダリングされます。

On the wire, the JSON will look something like this: {

    "kind":"MyAPIObject",
    "apiVersion":"v1",
    "myPlugin": {
        "kind":"PluginA",
        "aOption":"foo",
    },

}

もう1つのよくある間違いは、同様に「{」と「}」で囲まれた、インデントされていない Go 関数定義またはブロックステートメントでした。

Go 1.19 の gofmt でのドキュメントコメントの再フォーマットの導入により、コードブロックの周囲に空行が追加されることで、このような間違いが見やすくなります。

2022 年の分析では、公開されている Go モジュールのドキュメントコメントのわずか 3% のみが、ドラフトの Go 1.19 gofmt によって再フォーマットされたことがわかりました。これらのコメントに限定すると、gofmt の再フォーマットの約 87% は、コメントを読むことで人が推測する構造を保持していました。約 6% は、これらの種類のインデントされていないリスト、インデントされていない複数行のシェルコマンド、およびインデントされていない中括弧で囲まれたコードブロックによって混乱していました。

この分析に基づいて、Go 1.19 gofmt は、インデントされていない行を隣接するインデントされたリストまたはコードブロックにマージするためのいくつかのヒューリスティックを適用します。これらの調整により、Go 1.19 gofmt は上記の例を次のように再フォーマットします。

// cancelTimerBody is an io.ReadCloser that wraps rc with two features:
//  1. On Read error or close, the stop func is called.
//  2. On Read failure, if reqDidTimeout is true, the error is wrapped and
//     marked as net.Error that hit its timeout.

// localhostCert is a PEM-encoded TLS cert generated from src/crypto/tls:
//
//  go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \
//      --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h

// On the wire, the JSON will look something like this:
//
//  {
//      "kind":"MyAPIObject",
//      "apiVersion":"v1",
//      "myPlugin": {
//          "kind":"PluginA",
//          "aOption":"foo",
//      },
//  }

この再フォーマットにより、意味が明確になるだけでなく、以前のバージョンの Go でもドキュメントコメントが正しくレンダリングされるようになります。ヒューリスティックが間違った決定を下した場合、段落テキストと段落以外のテキストを明確に分離するために空行を挿入することで、オーバーライドできます。

これらのヒューリスティックを使用しても、他の既存のコメントはレンダリングを修正するために手動で調整する必要があります。最もよくある間違いは、折り返されたインデントされていないテキスト行をインデントすることです。例えば

// TODO Revisit this design. It may make sense to walk those nodes
//      only once.

// According to the document:
// "The alignment factor (in bytes) that is used to align the raw data of sections in
//  the image file. The value should be a power of 2 between 512 and 64 K, inclusive."

これらの両方で、最後の行はインデントされているため、コードブロックになります。修正するには、行のインデントを解除します。

もう1つのよくある間違いは、リストまたはコードブロックの折り返されたインデントされた行をインデントしないことです。例えば

// Uses of this error model include:
//
//   - Partial errors. If a service needs to return partial errors to the
// client,
//     it may embed the `Status` in the normal response to indicate the
// partial
//     errors.
//
//   - Workflow errors. A typical workflow has multiple steps. Each step
// may
//     have a `Status` message for error reporting.

修正するには、折り返された行をインデントします。

Go ドキュメントコメントはネストされたリストをサポートしていないため、gofmt は次のように再フォーマットします。

// Here is a list:
//
//  - Item 1.
//    * Subitem 1.
//    * Subitem 2.
//  - Item 2.
//  - Item 3.

次のように

// Here is a list:
//
//  - Item 1.
//  - Subitem 1.
//  - Subitem 2.
//  - Item 2.
//  - Item 3.

ネストされたリストを回避するようにテキストを書き直すと、通常はドキュメントが改善され、最良の解決策となります。別の潜在的な回避策は、リストマーカーを混在させることです。箇条書きマーカーは番号付きリストにリスト項目を導入せず、その逆も同様です。例えば

// Here is a list:
//
//  1. Item 1.
//
//     - Subitem 1.
//
//     - Subitem 2.
//
//  2. Item 2.
//
//  3. Item 3.