[インデックス 15335] ファイルの概要
このコミットは、Go言語の標準ライブラリであるfmt
パッケージにおける、nil
値のフォーマット時の幅指定に関するバグ修正です。具体的には、fmt.Printf
関数でnil
値をフォーマットする際に、幅指定(例: %10v
や%-10T
)が正しく適用されない問題を解決します。
コミット
commit cbd2c7a283b785f109dd350ef4d20dd9871d3359
Author: Robert Dinu <r@oktett.se>
Date: Wed Feb 20 14:30:15 2013 -0800
fmt: fix width for nil values
Apply width when using Printf with nil values.
Fixes #4772.
R=r, adg
CC=golang-dev
https://golang.org/cl/7314114
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cbd2c7a283b785f109dd350ef4d20dd9871d3359
元コミット内容
fmt: fix width for nil values
Apply width when using Printf with nil values.
Fixes #4772.
R=r, adg
CC=golang-dev
https://golang.org/cl/7314114
変更の背景
Go言語のfmt
パッケージは、C言語のprintf
に似た書式指定文字列を用いた出力機能を提供します。この機能には、出力する値の幅を指定する機能(例: %10v
で10文字分の幅を確保し、右寄せで出力)が含まれています。
このコミットが修正する問題は、fmt.Printf
がnil
値をフォーマットする際に、この幅指定が無視されてしまうというものでした。例えば、fmt.Printf("%10v", nil)
と記述した場合、期待される出力は <nil>
(5文字の<nil>
の前に5つのスペース)ですが、バグのあるバージョンでは単に<nil>
と出力されてしまい、幅指定が適用されませんでした。これは、nil
値の特殊な扱いが原因で、通常のパディング処理がスキップされていたためと考えられます。
この問題は、GoのIssue #4772として報告されていました。
前提知識の解説
Go言語のfmt
パッケージ
fmt
パッケージは、Go言語におけるフォーマットI/Oを実装するためのパッケージです。主に以下の関数がよく使われます。
fmt.Print
: 引数をデフォルトのフォーマットで出力します。fmt.Println
: 引数をデフォルトのフォーマットで出力し、改行を追加します。fmt.Printf
: 書式指定文字列と引数を用いてフォーマットされた文字列を出力します。
fmt.Printf
の書式指定
fmt.Printf
では、C言語のprintf
と同様に、様々な書式指定子(verb)を使用して出力形式を制御できます。
%v
: 値のデフォルトフォーマット。構造体や配列など、あらゆる型に対応します。%T
: 値の型名を出力します。- 幅指定:
%[幅]v
のように、数値で幅を指定できます。例えば、%10v
は10文字分の幅を確保し、デフォルトでは右寄せで出力します。 - フラグ:
-
フラグは左寄せを指定します。例えば、%-10v
は10文字分の幅を確保し、左寄せで出力します。
nil
値
Go言語におけるnil
は、ポインタ、インターフェース、マップ、スライス、チャネル、関数などのゼロ値を表す特別な識別子です。これらの型の変数が初期化されていない状態や、有効な値を指していない状態を示します。nil
は特定の型に属するものではなく、これらの型の「値がない」状態を表現します。
技術的詳細
この修正は、fmt
パッケージ内部のpp
(printer)構造体のprintField
メソッドに焦点を当てています。printField
メソッドは、fmt.Printf
が個々の引数をフォーマットする際に呼び出される内部関数です。
修正前のコードでは、field
がnil
である場合に、verb
が'T'
または'v'
であれば、p.buf.Write(nilAngleBytes)
を直接呼び出していました。ここでnilAngleBytes
は文字列<nil>
を表すバイトスライスです。このWrite
メソッドは、単にバッファにバイトを書き込むだけであり、幅指定やパディングのロジックは含まれていませんでした。
一方、通常の(nil
ではない)値のフォーマットでは、p.fmt.pad
メソッドが使用されます。このpad
メソッドは、指定された幅に基づいてパディング(スペースの追加)を行う責任を負っています。
このコミットの目的は、nil
値のフォーマットにおいても、通常の値と同様に幅指定が適用されるようにすることです。
コアとなるコードの変更箇所
変更はsrc/pkg/fmt/print.go
ファイルにあります。
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -729,7 +729,7 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
if field == nil {
if verb == 'T' || verb == 'v' {
- p.buf.Write(nilAngleBytes)
+ p.fmt.pad(nilAngleBytes)
} else {
p.badVerb(verb)
}
また、この変更を検証するためのテストケースがsrc/pkg/fmt/fmt_test.go
に追加されています。
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -178,6 +178,8 @@ var fmttests = []struct {
{"%.3q", "日本語日本語", `"日本語"`},
{"%.3q", []byte("日本語日本語"), `"日本語"`},
{"%10.1q", "日本語日本語", ` "日"`},
+ {"%10v", nil, " <nil>"},
+ {"%-10v", nil, "<nil> "},
// integers
{"%d", 12345, "12345"},
@@ -437,6 +439,8 @@ var fmttests = []struct {
{"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"},
{"%T", intVal, "int"},
{"%6T", &intVal, " *int"},
+ {"%10T", nil, " <nil>"},
+ {"%-10T", nil, "<nil> "},
// %p
{"p0=%p", new(int), "p0=0xPTR"},
コアとなるコードの解説
src/pkg/fmt/print.go
の変更は非常にシンプルですが、その影響は大きいです。
修正前:
p.buf.Write(nilAngleBytes)
この行は、nil
値が%v
または%T
でフォーマットされる際に、直接<nil>
という文字列を内部バッファに書き込んでいました。この操作は、幅指定やパディングのロジックを完全にバイパスしていました。
修正後:
p.fmt.pad(nilAngleBytes)
この行では、p.buf.Write
の代わりにp.fmt.pad
が呼び出されています。p.fmt
はfmt
パッケージのフォーマッタ(fmt
構造体)への参照であり、pad
メソッドは、現在のフォーマット設定(幅、精度、フラグなど)に基づいて、与えられたバイトスライス(この場合は<nil>
を表すnilAngleBytes
)を適切にパディングして出力する役割を担っています。
この変更により、nil
値がフォーマットされる際にも、他の値と同様にfmt
パッケージのパディングロジックが適用されるようになり、幅指定が正しく機能するようになりました。
src/pkg/fmt/fmt_test.go
に追加されたテストケースは、この修正が意図通りに機能することを確認します。
{"%10v", nil, " <nil>"}
:%10v
でnil
をフォーマットした場合、10文字幅で右寄せされ、<nil>
の前に5つのスペースが追加されることを期待します。{"%-10v", nil, "<nil> "}
:%-10v
でnil
をフォーマットした場合、10文字幅で左寄せされ、<nil>
の後に5つのスペースが追加されることを期待します。- 同様に、
%T
に対しても幅指定が適用されることを確認するテストケースが追加されています。
これらのテストケースは、修正が正しく機能していることを検証するための重要な役割を果たします。
関連リンク
- Go言語の
fmt
パッケージ公式ドキュメント: https://pkg.go.dev/fmt - Go言語のIssueトラッカー: https://github.com/golang/go/issues
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/15335.txt
- GitHub上のコミットページ: https://github.com/golang/go/commit/cbd2c7a283b785f109dd350ef4d20dd9871d3359
- Go言語の
fmt
パッケージのソースコード(当時のバージョンに近いもの) - Go言語の
fmt
パッケージの書式指定に関する一般的な情報 - Go言語における
nil
の概念に関する一般的な情報 - Go issue #39886 (関連する可能性のある議論): https://github.com/golang/go/issues/39886 (ただし、直接のIssue #4772は見つからなかったため、一般的な
fmt
の挙動に関する議論として参照)