testing: allow examples to return an error
要約
概要
testingパッケージのExample関数(ExampleXxx形式)がerrorを戻り値として返せるようにするproposalです。現在はExample関数が戻り値を持てないため、エラー処理のコードが煩雑になり、実際のアプリケーションコードを代表するサンプルとして機能しにくい問題を解決します。
ステータス変更
**** → active
2026年5月27日のProposal Review Meetingにて、aclements氏がこのproposalをactiveカラムに追加し、週次レビューの対象としました。2018年に「Go 2のエラーハンドリング構文改善を待つ」として保留(hold)とされていましたが、エラーハンドリング構文改善が無期限に延期されると決定されたため(2025年12月にadonovan氏がholdラベルを除去)、改めてactiveとして議論が再開されることになりました。
技術的背景
現状の問題点
Example関数は現在、戻り値を持てません。そのため、エラーを返す関数を呼び出すコードを含む例では、以下のような不自然な実装を強いられます。
// 現在の書き方: panicやlog.Fatalに頼るか、内部関数でラップする必要がある
func ExampleWriteTempFile() {
example := func() (err error) {
filename, cleanup, err := WriteTempFile("", "example-*", []byte("example"))
if err != nil {
return err
}
defer cleanup(&err)
fmt.Printf("Temporary example file name: %s\n", filename)
return nil
}
err := example()
if err != nil {
panic(err)
}
// Output: ...
}
標準ライブラリの例(os.CreateTempなど)でも、deferとlog.Fatalを組み合わせており、deferが実行されないコードが含まれるという不自然な状態が生まれています。
提案された解決策
Example関数がオプションでerrorを戻り値として返せるようにします。ianlancetaylor氏が早期に指摘したように、// Output:または// Unordered output:コメントを持つExample関数と同様に実行され、非nilのerrorが返された場合はテスト失敗と判定されます。
adonovan氏が2026年5月にまとめた最終仕様:
- Example関数がオプションで
errorを返せるようにする // Output:または// Unordered output:コメントを持つ場合はgo testで実行される(現在と同様)- 非nilのerrorが返された場合はExample失敗
- nilが返された場合は出力を通常通り検証
Test関数やBenchmark関数への変更はなし
これによって何ができるようになるか
エラーを返す関数を呼び出すサンプルコードが、実際のアプリケーションコードに近い自然な形で書けるようになります。
コード例
// Before: panicやlog.Fatalを使った不自然な書き方
func ExampleWriteTempFile() {
filename, cleanup, err := WriteTempFile("", "example-*", []byte("example"))
if err != nil {
log.Fatal(err) // deferが実行されない問題が生じる
}
defer cleanup(nil)
fmt.Printf("Temporary example file name: %s\n", filename)
// Output: Temporary example file name: /tmp/example-12345
}
// After: エラーをそのまま返せるシンプルな書き方
func ExampleWriteTempFile() error {
filename, cleanup, err := WriteTempFile("", "example-*", []byte("example"))
if err != nil {
return err
}
defer cleanup(&err)
fmt.Printf("Temporary example file name: %s\n", filename)
// Output: Temporary example file name: /tmp/example-12345
}
実践的なメリットとして以下が挙げられます。
- ドキュメント品質の向上: GoDocやpkg.go.devで表示されるサンプルコードが実際のプロダクションコードのパターン(エラーを
returnで上位に委譲する)を正しく反映できる - deferとの整合性:
log.Fatalを使わずに済むため、deferを含む例が正しく動作するサンプルを書ける - 学習コストの低減: Go初学者がサンプルコードを見て「なぜpanicしているのか」「なぜlog.Fatalなのか」と混乱しなくて済む
議論のハイライト
- 2018年のhold:
rsc氏がGo 2のエラーハンドリング構文改善を待つべきとしてholdに設定。しかしその後、エラーハンドリング構文改善自体が無期限延期となり(2025年)、holdの根拠が消滅した return nilの必要性の懸念:dmitshur氏は最後にreturn nilが必須となる点を懸念したが、rogpeppe氏は「関数宣言も表示しないGodocと同様に、末尾のreturn nilも省略表示できる」と回答- 表示形式の課題:
neild氏(2026年5月)がpkg.go.devでのコード表示への影響を指摘。_testパッケージ内のExample関数はfunc main()として表示されるが、errorを返す関数はそのままmain関数に変換できないため、表示方法の検討が必要 - TestやBenchmarkへの拡張の議論:
aclements氏は関連Issue #64993(TestにTestingT引数を許可する提案)との整合性に言及し、Testへのerrorリターン対応も将来的に考えられると述べた - ワークアラウンドの複雑さ:
marco-m氏やその他の貢献者が現在の回避策(内部ヘルパー関数でラップする、panicする、log.Fatalを使う)の不自然さを具体的な例とともに示し、提案の必要性を裏付けた