[インデックス 16945] ファイルの概要
このコミットは、Go言語の標準ライブラリであるfmt
パッケージにおけるエラーメッセージのフォーマットを統一することを目的としています。具体的には、fmt
パッケージが生成する全てのエラーメッセージが、常に%!
という文字列で始まるように変更されています。これにより、エラーの識別と解析が容易になります。
コミット
commit 53b61057b2ca71cc92ccdd6e7c91caf3fb00c3ef
Author: Robert Daniel Kortschak <dan.kortschak@adelaide.edu.au>
Date: Wed Jul 31 16:11:12 2013 +1000
fmt: make all errors begin with the string "%!", always.
Fixes #5730.
R=dsymonds, r, kamil.kisiel
CC=golang-dev
https://golang.org/cl/11998044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/53b61057b2ca71cc92ccdd6e7c91caf3fb00c3ef
元コミット内容
fmt: make all errors begin with the string "%!", always.
このコミットは、fmt
パッケージが生成する全てのエラーメッセージが、常に%!
という文字列で始まるように変更します。
変更の背景
Go言語のfmt
パッケージは、文字列のフォーマットとスキャンを行うための機能を提供します。Printf
のような関数を使用する際に、不正なフォーマット指定子や引数の不一致などが発生した場合、fmt
パッケージはエラーメッセージを生成して出力します。
このコミットが修正しようとしている問題は、これらのエラーメッセージのプレフィックスが一貫していなかったことです。例えば、以前は%d!(BADINDEX)
のような形式のエラーメッセージが存在しましたが、これは%!
で始まっていませんでした。このような一貫性の欠如は、プログラムによるエラーメッセージのパースや、ユーザーがエラーメッセージを見たときにそれがfmt
パッケージによって生成されたものであると即座に識別することを困難にしていました。
コミットメッセージにあるFixes #5730
は、この変更が特定の課題(Issue 5730)を解決するものであることを示しています。この課題の具体的な内容は不明ですが、エラーメッセージの一貫性に関するものであったと推測されます。
前提知識の解説
Go言語のfmt
パッケージ
fmt
パッケージは、C言語のprintf
やscanf
に似た機能を提供し、Goプログラム内で文字列のフォーマット(整形)やスキャン(解析)を行います。主な関数にはPrintf
、Sprintf
、Fprintf
、Scanf
などがあります。
フォーマット指定子
fmt
パッケージでは、%d
(整数)、%s
(文字列)、%v
(デフォルトフォーマット)などのフォーマット指定子を使用して、変数の値を特定の形式で出力します。これらの指定子には、幅(%w
)、精度(.p
)、引数インデックス(%[n]
)などのオプションを付加することができます。
fmt
パッケージのエラー処理
fmt
パッケージは、フォーマット指定子が不正であったり、引数の型が期待と異なったり、引数が不足していたりする場合に、エラーメッセージを生成して出力文字列に含めます。これらのエラーメッセージは、通常、%!
で始まる特殊な形式で表示され、開発者が問題の原因を特定するのに役立ちます。
例えば、Printf("%d", "hello")
のように整数を期待するフォーマット指定子に文字列を渡した場合、fmt
パッケージは%!d(string=hello)
のようなエラーメッセージを生成することがあります。これは、%d
が期待されたが、実際には文字列が渡されたことを示しています。
panic
とfmt
パッケージ
Go言語では、回復不可能なエラーが発生した場合にpanic
が使用されます。fmt
パッケージのフォーマット処理中に、引数として渡された値のString()
メソッドやFormat()
メソッドがpanic
を起こすことがあります。このような場合、fmt
パッケージはPANIC=
というプレフィックスを含むエラーメッセージを生成し、元のpanic
の情報を表示します。
技術的詳細
このコミットの主要な目的は、fmt
パッケージが生成する全てのエラーメッセージの先頭に%!
を強制することです。以前は、一部のエラーメッセージ(特にBADINDEX
やPANIC
関連のエラー)が%!
で始まっていませんでした。
この変更は、主に以下の3つのファイルに影響を与えています。
src/pkg/fmt/doc.go
:fmt
パッケージのドキュメントファイルです。エラーメッセージの例が更新され、全てのエラーが%!
で始まるように修正されています。これは、この変更がユーザー向けのドキュメントにも反映されることを意味します。src/pkg/fmt/fmt_test.go
:fmt
パッケージのテストファイルです。エラーケースのテスト期待値が更新され、新しいエラーメッセージのフォーマット(%!
で始まる形式)に一致するように変更されています。これにより、変更が正しく実装され、既存の機能が損なわれていないことが保証されます。src/pkg/fmt/print.go
:fmt
パッケージのコアなフォーマットロジックが含まれるファイルです。このファイルで、実際にエラーメッセージの生成方法が変更されています。
具体的な変更点としては、エラーメッセージを構成するバイトスライス(badIndexBytes
, panicBytes
, missingBytes
など)の定義が調整され、これらのバイトスライスが%!
を含まない形に変更されました。その代わりに、percentBangBytes
という新しいバイトスライスが導入され、これが%!
という文字列を保持します。
そして、エラーメッセージをバッファに書き込む際に、まずpercentBangBytes
を書き込み、その後にエラーの種類を示すバイトスライス(例: badIndexBytes
)を書き込むようにロジックが変更されました。これにより、全てのエラーメッセージが%!
で始まることが保証されます。
例えば、以前はp.buf.WriteByte('%')
とp.buf.Write(badIndexBytes)
のように分けて書き込んでいた箇所が、p.buf.Write(percentBangBytes)
とp.buf.Write(badIndexBytes)
のように変更されています。これにより、%
の後に!
が続くことが保証されます。
コアとなるコードの変更箇所
このコミットの主要な変更は、src/pkg/fmt/print.go
ファイルに集中しています。
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -16,20 +16,21 @@ import (
// Some constants in the form of bytes, to avoid string overhead.
// Needlessly fastidious, I suppose.
var (
- commaSpaceBytes = []byte(", ")
- nilAngleBytes = []byte("<nil>")
- nilParenBytes = []byte("(nil)")
- nilBytes = []byte("nil")
- mapBytes = []byte("map[")
- missingBytes = []byte("!(MISSING)")
- badIndexBytes = []byte("!(BADINDEX)")
- panicBytes = []byte("!(PANIC=")
- extraBytes = []byte("%!(EXTRA ")
- irparenBytes = []byte("i)")
- bytesBytes = []byte("[]byte{")
- badWidthBytes = []byte("%!(BADWIDTH)")
- badPrecBytes = []byte("%!(BADPREC)")
- noVerbBytes = []byte("%!(NOVERB)")
+ commaSpaceBytes = []byte(", ")
+ nilAngleBytes = []byte("<nil>")
+ nilParenBytes = []byte("(nil)")
+ nilBytes = []byte("nil")
+ mapBytes = []byte("map[")
+ percentBangBytes = []byte("%!") // 新しく追加されたバイトスライス
+ missingBytes = []byte("(MISSING)") // "%!"が削除された
+ badIndexBytes = []byte("(BADINDEX)") // "%!"が削除された
+ panicBytes = []byte("(PANIC=") // "%!"が削除された
+ extraBytes = []byte("%!(EXTRA ")
+ irparenBytes = []byte("i)")
+ bytesBytes = []byte("[]byte{")
+ badWidthBytes = []byte("%!(BADWIDTH)")
+ badPrecBytes = []byte("%!(BADPREC)")
+ noVerbBytes = []byte("%!(NOVERB)")
)
// State represents the printer state passed to custom formatters.
@@ -660,12 +661,12 @@ func (p *pp) catchPanic(arg interface{}, verb rune) {
// Nested panics; the recursion in printArg cannot succeed.
panic(err)
}
- p.buf.WriteByte('%') // 変更前: '%'を直接書き込んでいた
+ p.buf.Write(percentBangBytes) // 変更後: '%!'を書き込む
p.add(verb)
p.buf.Write(panicBytes)
p.panicking = true
}
@@ -1165,12 +1166,12 @@ func (p *pp) doPrintf(format string, a []interface{}) {
continue
}
if !p.goodArgNum {
- p.buf.WriteByte('%') // 変更前: '%'を直接書き込んでいた
+ p.buf.Write(percentBangBytes) // 変更後: '%!'を書き込む
p.add(c)
p.buf.Write(badIndexBytes)
continue
} else if argNum >= len(a) { // out of operands
- p.buf.WriteByte('%') // 変更前: '%'を直接書き込んでいた
+ p.buf.Write(percentBangBytes) // 変更後: '%!'を書き込む
p.add(c)
p.buf.Write(missingBytes)
continue
コアとなるコードの解説
この変更の核心は、fmt
パッケージがエラーメッセージを構築する際のバイトスライスの管理と書き込みロジックの変更にあります。
-
percentBangBytes
の導入: 以前は、%!
で始まるエラーメッセージの一部(例:badWidthBytes
,badPrecBytes
,noVerbBytes
)は、そのバイトスライス自体が%!
を含んでいました。しかし、badIndexBytes
,panicBytes
,missingBytes
などは%!
を含んでいませんでした。 このコミットでは、percentBangBytes = []byte("%!")
という新しいバイトスライスが導入されました。これにより、%!
というプレフィックスを独立したコンポーネントとして扱うことができるようになりました。 -
エラーメッセージバイトスライスの変更:
missingBytes
、badIndexBytes
、panicBytes
などのバイトスライスから、先頭の%!
が削除されました。例えば、badIndexBytes
は以前"!(BADINDEX)"
でしたが、変更後は"(BADINDEX)"
となりました。これは、これらのバイトスライスがエラーの種類を示す部分のみを保持するようにするためです。 -
エラーメッセージ書き込みロジックの統一:
catchPanic
関数やdoPrintf
関数内でエラーメッセージをバッファ(p.buf
)に書き込む際、以前はp.buf.WriteByte('%')
のように%
を個別に書き込んでから、エラーの種類を示すバイトスライスを書き込んでいました。 変更後は、p.buf.Write(percentBangBytes)
を呼び出すことで、常に%!
が最初にバッファに書き込まれるようになりました。その後に、エラーの種類を示すバイトスライス(例:panicBytes
,badIndexBytes
,missingBytes
)が書き込まれます。
この変更により、fmt
パッケージが生成する全てのエラーメッセージは、一貫して%!
で始まるようになります。これは、エラーメッセージのパースを容易にし、開発者がfmt
パッケージからのエラーを迅速に識別できるようにするための重要な改善です。
関連リンク
- Go Gerrit Change-ID: https://golang.org/cl/11998044
参考にした情報源リンク
- Go言語の
fmt
パッケージに関する公式ドキュメント - Go言語のソースコード(
src/pkg/fmt/
ディレクトリ) - Go言語の
panic
とエラー処理に関する一般的な知識