パッケージ
すべてのパッケージには、パッケージを紹介するパッケージコメントが必要です。これは、パッケージ全体に関連する情報を提供し、一般的にパッケージに対する期待を設定します。特に大きなパッケージでは、パッケージコメントが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
この例に見られるように、Goのドキュメントコメントは完全な文を使用します。パッケージコメントの場合、[最初の文](https://golang.org/pkg/go/doc/#Package.Synopsis)は「パッケージ \u003cname>」で始まります。\u003c/name>
複数ファイルのパッケージの場合、パッケージコメントは1つのソースファイルにのみ存在する必要があります。複数のファイルにパッケージコメントがある場合、それらは連結されてパッケージ全体の1つの大きなコメントを形成します。
<a name="cmd"></a>
## コマンド
コマンドのパッケージコメントは似ていますが、パッケージ内のGoシンボルではなく、プログラムの動作を説明します。最初の文は通常、プログラム自体の名前で始まり、文の最初にあるため大文字になります。例えば、[gofmt](https://golang.org/cmd/gofmt)のパッケージコメントの要約版は次のとおりです:
``````bash
/*
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
`
コメントの最初は、セマンティックラインフィードを使用して書かれています。ここでは、新しい文や長いフレーズがそれぞれ独自の行にあり、コードとコメントが進化するにつれてdiffを読みやすくすることができます。後の段落はこの規則に従っていない場合があり、手動でラップされています。あなたのコードベースにとって最適な方法であれば問題ありません。いずれにせよ、go
doc
および pkgsite
は、印刷時にドキュメントコメントテキストを再ラップします。例えば:
$ 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
...
}
パッケージ(上記)や関数(以下)と同様に、型のドキュメントコメントは宣言されたシンボルの名前を持つ完全な文で始まります。明示的な主語は、表現を明確にし、テキストをウェブページやコマンドラインで検索しやすくします。例えば:
$ 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の宣言構文は、宣言のグループ化を許可します。この場合、単一のドキュメントコメントが関連する定数のグループを紹介でき、個々の定数は短い行末コメントでのみ文書化されます。例えば:
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
に再フォーマットすることに類似しています。
``````bash
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はその接頭辞を削除します。
段落
段落は、インデントされていない非空白行の範囲です。すでに多くの段落の例を見てきました。
連続するバックティック(` U+0060)は、Unicodeの左引用符(“ U+201C)として解釈され、連続するシングルクォート(\u0026#39; 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では、\u0026lt;a href=“URL”\u0026gt;テキスト \u0026lt;/a\u0026gt;。例えば:
// 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://golang.org/doc/articles/json_and_go.html
package json
URLを別のセクションに保持することで、この形式は実際のテキストの流れを最小限に中断します。また、Markdownのショートカットリファレンスリンク形式に大まかに一致しますが、オプションのタイトルテキストはありません。
対応するURL宣言がない場合、(次のセクションで説明するドキュメントリンクを除いて)「[テキスト]」はハイパーリンクではなく、表示時に角括弧が保持されます。各ドキュメントコメントは独立して考慮されます:1つのコメント内のリンクターゲット定義は他のコメントに影響を与えません。
リンクターゲット定義ブロックは、通常の段落と交互に配置される場合がありますが、gofmtはすべてのリンクターゲット定義をドキュメントコメントの最後に移動し、最大2つのブロックに分けます。最初のブロックには、コメント内で参照されているすべてのリンクターゲットが含まれ、次のブロックには、コメント内で参照されていないすべてのターゲットが含まれます。別のブロックにすることで、未使用のターゲットを簡単に見つけて修正(リンクや定義に誤字がある場合)したり、削除(定義がもはや必要ない場合)したりできます。
URLとして認識されるプレーンテキストは、HTMLレンダリングで自動的にリンクされます。
ドキュメントリンク
ドキュメントリンクは、「[Name1]」または「[Name1.Name2]」の形式で、現在のパッケージ内のエクスポートされた識別子を参照するためのリンク、または「[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をインポートしている場合、「[json.Decoder]」は「[encoding/json.Decoder]」の代わりに書くことができ、encoding/jsonのDecoderのドキュメントにリンクします。パッケージ内の異なるソースファイルが同じ名前を使用して異なるパッケージをインポートしている場合、ショートハンドは曖昧であり、使用できません。
「pkg」は、ドメイン名(ドットを含むパス要素)で始まる場合、または標準ライブラリのパッケージのいずれか(「[os]」、「[encoding/json]」など)の場合にのみ、完全なインポートパスであると仮定されます。例えば、[os.File]
および[example.com/sys.File]
はドキュメントリンクですが(後者は壊れたリンクになります)、[os/sys.File]
はそうではありません。なぜなら、標準ライブラリにはos/sysパッケージが存在しないからです。
マップ、ジェネリクス、および配列型に関する問題を避けるために、ドキュメントリンクは、句読点、スペース、タブ、または行の開始または終了の前後に配置される必要があります。例えば、「map[ast.Expr]TypeAndValue」というテキストにはドキュメントリンクが含まれていません。
リスト
リストは、インデントされた行または空白行の範囲(次のセクションで説明するコードブロックであるはずのもの)であり、最初のインデントされた行は、バレットリストマーカーまたは番号付きリストマーカーで始まります。
バレットリストマーカーは、アスタリスク、プラス、ダッシュ、またはUnicodeバレット(*、+、-、•; U+002A、U+002B、U+002D、U+2022)であり、その後にスペースまたはタブ、そしてテキストが続きます。バレットリストでは、バレットリストマーカーで始まる各行が新しいリスト項目を開始します。
例えば:
package url
// PublicSuffixList provides the public suffix of a domain. 例えば:
// - 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では
- ブロック)。
コードブロックには、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の初期の頃からto存在しています。残念ながら、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モジュールのドキュメントコメントのうち、ドラフトGo 1.19のgofmtによって再フォーマットされたのはわずか3%でした。これらのコメントに限ると、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.