[インデックス 10186] ファイルの概要
このコミットは、Go言語のcgo
ツールとgotest
ツールにおいて、生成されるコード内でエラーハンドリングに使用される型をos.Error
から標準のerror
インターフェースへ変更するものです。これは、Go言語のエラーハンドリングの進化と、os.Error
が非推奨になったことによる対応です。
コミット
- コミットハッシュ:
c8ad1a4dc4d0384d963df749cfc3c373e27d6a17
- Author: Russ Cox rsc@golang.org
- Date: Tue Nov 1 21:49:22 2011 -0400
- コミットメッセージ:
cgo, gotest: use error instead of os.Error in generated code R=golang-dev, iant CC=golang-dev https://golang.org/cl/5319057
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c8ad1a4dc4d0384d963df749cfc3c373e27d6a17
元コミット内容
cgo, gotest: use error instead of os.Error in generated code
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/5319057
変更の背景
Go言語の初期のバージョンでは、エラーを表すためにos.Error
という具体的な型が使用されていました。しかし、Go言語のエラーハンドリングの設計思想は、より柔軟で汎用的なインターフェースベースのアプローチへと進化しました。その結果、os.Error
は非推奨となり、代わりに組み込みのerror
インターフェースを使用することが推奨されるようになりました。
このコミットは、cgo
(C言語のコードをGoから呼び出すためのツール)とgotest
(Goのテストフレームワーク)が生成するコードが、この新しいエラーハンドリングの慣習に準拠するように修正するためのものです。具体的には、cgo
がC関数呼び出しのエラーをGoのコードに変換する際や、gotest
がテスト実行時に使用する内部コードで、os.Error
ではなくerror
インターフェースを使用するように変更されています。これにより、Go言語全体のエラーハンドリングの一貫性が保たれ、将来的な互換性も確保されます。
前提知識の解説
Go言語のエラーハンドリングの進化
Go言語のエラーハンドリングは、他の多くの言語に見られるような例外処理(try-catchなど)とは異なり、関数の戻り値としてエラーを明示的に返すスタイルを採用しています。
-
os.Error
(旧来の方式): Go言語の非常に初期の段階では、os
パッケージ内にError
という型が存在し、エラーを表すために使われていました。これは、特定のパッケージに依存する具体的な型であり、Goのエラーハンドリングの哲学である「インターフェースによる抽象化」とは少し異なるアプローチでした。os.Error
は、エラーコードやエラーメッセージを保持する構造体のようなものでした。 -
error
インターフェース (現在の標準): Go言語の現在の標準的なエラーハンドリングは、組み込みのerror
インターフェースに基づいています。このインターフェースは非常にシンプルで、Error() string
という単一のメソッドを定義しています。type error interface { Error() string }
このインターフェースを実装する任意の型は、エラーとして扱うことができます。これにより、開発者は独自のカスタムエラー型を定義し、より詳細なエラー情報を提供したり、エラーの種類に基づいて異なる処理を行ったりすることが可能になります。関数がエラーを返す場合、通常は最後の戻り値として
error
型を返します。エラーがない場合はnil
を返します。os.Error
からerror
インターフェースへの移行は、Go言語がより柔軟で、拡張性があり、かつ一貫性のあるエラーハンドリングメカニズムを提供するための重要なステップでした。
cgo
とは
cgo
は、GoプログラムからC言語のコードを呼び出すためのGoツールチェーンの一部です。GoとCの間の相互運用性を提供し、既存のCライブラリをGoプロジェクトで利用できるようにします。cgo
は、Goのソースファイル内にCのコードを直接記述したり、Cのヘッダーファイルをインポートしたりすることで、GoとCの間のブリッジコードを自動生成します。
cgo
がC関数をGoから呼び出すためのラッパーコードを生成する際、Cのerrno
(システムコールが失敗した際に設定されるエラー番号)をGoのエラーとして扱う必要があります。このコミット以前は、このerrno
をos.Error
としてラップしていましたが、変更後はerror
インターフェースとしてラップするようになります。
gotest
とは
gotest
は、Go言語のテストフレームワークの実行を管理するツールです。Goのテストは、通常、_test.go
というサフィックスを持つファイルに記述され、go test
コマンドによって実行されます。gotest
は、これらのテストファイルをコンパイルし、テストバイナリを実行し、結果を報告する役割を担います。
このコミットでは、gotest
がテスト実行のために内部的に生成するコード(例えば、テストの正規表現マッチング関数など)において、エラー型をos.Error
からerror
インターフェースに修正しています。
技術的詳細
このコミットの技術的な核心は、Go言語のエラーハンドリングの標準化と、それに伴うツールチェーンの更新です。
-
os.Error
からerror
インターフェースへの移行: Go言語の設計思想では、インターフェースは「振る舞い」を定義し、具体的な実装はそれに従います。error
インターフェースは、エラーが持つべき最小限の振る舞い(エラーメッセージを文字列として返すこと)を定義しています。これにより、どのようなエラーであっても、そのメッセージを取得できるという一貫性が保証されます。os.Error
のような具体的な型に依存するのではなく、error
インターフェースに依存することで、cgo
やgotest
が生成するコードは、Goのエコシステム全体で利用されるカスタムエラー型や、将来的に導入される可能性のある新しいエラー処理メカニズムともシームレスに連携できるようになります。 -
cgo
におけるCerrno
の扱い: C言語の関数がエラーを返した場合、通常はグローバル変数errno
にエラーコードが設定されます。cgo
は、このerrno
をGoのエラーとしてGoのコードに公開する機能を持っています。このコミットでは、_Cerrno
という内部関数がdst *os.Error
を受け取る代わりにdst *error
を受け取るように変更されています。これにより、Cのerrno
がGoのerror
インターフェースとして適切に扱われるようになります。 -
gotest
における内部コードの修正:gotest
は、テストの実行を効率化するために、テストバイナリに組み込まれる補助的なGoコードを生成します。例えば、テスト名の正規表現マッチングを行うmatchString
関数などです。このmatchString
関数も、エラーを返す際にos.Error
ではなくerror
インターフェースを使用するように変更されています。これにより、テストフレームワーク自体がGoのエラーハンドリングのベストプラクティスに準拠します。 -
import "os"
の削除:os.Error
が使用されなくなったため、gotest
の生成コードからimport "os"
が不要になりました。これは、不要な依存関係を削除し、生成されるコードをよりクリーンにするための変更です。
これらの変更は、Go言語の進化に合わせて、その基盤となるツールチェーンも継続的に更新されていることを示しています。これにより、Go言語のコードベース全体の一貫性と保守性が向上します。
コアとなるコードの変更箇所
このコミットでは、以下の3つのファイルが変更されています。
src/cmd/cgo/doc.go
:cgo
のドキュメントファイル。os.Error
に関する記述がerror
に修正されています。src/cmd/cgo/out.go
:cgo
がGoコードを生成する際のロジックが含まれるファイル。os.Error
型を使用していた箇所がerror
インターフェースに置き換えられています。src/cmd/gotest/gotest.go
:gotest
ツールの主要なロジックが含まれるファイル。テスト実行時に生成されるコード内でos.Error
を使用していた箇所がerror
インターフェースに置き換えられ、不要になったos
パッケージのインポートが削除されています。
コアとなるコードの解説
src/cmd/cgo/doc.go
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -59,7 +59,7 @@ struct_, union_, or enum_, as in C.struct_stat.
Any C function that returns a value may be called in a multiple
assignment context to retrieve both the return value and the
-C errno variable as an os.Error. For example:
+C errno variable as an error. For example:
n, err := C.atoi("abc")
この変更はドキュメントの修正です。cgo
がCのerrno
変数をGoのエラーとして返す際に、それがos.Error
ではなくerror
インターフェースとして扱われることを明記しています。これは、ユーザーがcgo
の挙動を正しく理解するための重要な情報です。
src/cmd/cgo/out.go
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -48,7 +48,7 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "import \"os\"\\n\\n")
fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\\n\\n")
fmt.Fprintf(fgo2, "type _ unsafe.Pointer\\n\\n")
- fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\\n")
+ fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = os.Errno(x) }\\n")
for name, def := range typedef {
fmt.Fprintf(fgo2, "type %s ", name)
@@ -203,7 +203,7 @@ func (p *Package) structType(n *Name) (string, int64) {
off += pad
}
if n.AddError {
- fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\\n")
+ fmt.Fprint(&buf, "\t\tvoid *e[2]; /* error */\\n")
off += 2 * p.PtrSize
}
if off == 0 {
@@ -217,9 +217,9 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
name := n.Go
gtype := n.FuncType.Go
if n.AddError {
- // Add "os.Error" to return type list.
+ // Add "error" to return type list.
// Type list is known to be 0 or 1 element - it's a C function.
- err := &ast.Field{Type: ast.NewIdent("os.Error")}
+ err := &ast.Field{Type: ast.NewIdent("error")}
l := gtype.Results.List
if len(l) == 0 {
l = []*ast.Field{err}
このファイルでは、cgo
が生成するGoコードのテンプレートが変更されています。
_Cerrno
関数のシグネチャがdst *os.Error
からdst *error
に変更されています。この関数は、Cのerrno
値をGoのerror
型に変換して設定するために使用されます。- Cの構造体内でエラー情報を保持するためのコメントが
/* os.Error */
から/* error */
に変更されています。これは、生成されるCコード内のコメントであり、Goのエラー型がerror
インターフェースであることを示唆しています。 writeDefsFunc
関数内で、C関数がエラーを返す場合にGoの戻り値リストにos.Error
を追加していた箇所が、error
を追加するように変更されています。これは、cgo
がC関数呼び出しのGoラッパーを生成する際に、エラーの戻り値型を正しく設定するための重要な変更です。
src/cmd/gotest/gotest.go
--- a/src/cmd/gotest/gotest.go
+++ b/src/cmd/gotest/gotest.go
@@ -401,7 +401,6 @@ func writeTestmainGo() {
fmt.Fprintf(b, "import target_test %q\\n", "./_xtest_")
}
fmt.Fprintf(b, "import %q\\n", "testing")
- fmt.Fprintf(b, "import %q\\n", "os")
fmt.Fprintf(b, "import %q\\n", "regexp")
fmt.Fprintln(b) // for gofmt
@@ -454,7 +453,7 @@ var testBody = `
var matchPat string
var matchRe *regexp.Regexp
-func matchString(pat, str string) (result bool, err os.Error) {
+func matchString(pat, str string) (result bool, err error) {
if matchRe == nil || matchPat != pat {
matchPat = pat
matchRe, err = regexp.Compile(matchPat)
このファイルでは、gotest
がテスト実行のために生成する_testmain.go
のような内部コードのテンプレートが変更されています。
import "os"
の行が削除されています。これは、os.Error
が使用されなくなったため、os
パッケージへの依存が不要になったためです。matchString
関数のシグネチャがfunc matchString(...) (result bool, err os.Error)
からfunc matchString(...) (result bool, err error)
に変更されています。この関数は、テスト名の正規表現マッチングに使用される補助関数であり、エラーを返す際にerror
インターフェースを使用するように修正されています。
これらの変更により、cgo
とgotest
が生成するGoコードは、Go言語の最新のエラーハンドリングの慣習に準拠し、error
インターフェースを介してエラーを処理するようになります。
関連リンク
- Go CL 5319057: https://golang.org/cl/5319057
参考にした情報源リンク
- Go言語のエラーハンドリングに関する公式ドキュメント: https://go.dev/blog/error-handling-and-go
- Go言語の
error
インターフェースについて: https://go.dev/blog/errors-are-values - Go言語のエラーハンドリングの基本: https://gobyexample.com/errors
- Go言語における
os.Error
の非推奨化とerror
インターフェースへの移行に関する議論や記事 (Web検索結果より):