[インデックス 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とエラー処理に関する一般的な知識