[インデックス 10180] ファイルの概要
本解説は、Go言語の標準ライブラリにerrors
パッケージが導入されたコミット(インデックス10180)について詳細に説明します。このコミットは、Go言語におけるエラーハンドリングの基本的なメカニズムを確立する上で重要な一歩となりました。
コミット
このコミットは、Go言語の標準ライブラリにerrors
パッケージを新規追加するものです。現時点ではerrors.New
関数のみを提供しており、これにより任意の文字列からerror
型の値を生成できるようになります。これは、Go言語におけるエラー表現の基礎を築くものです。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e4ae30f5f55b6b6ba2cb9a0c0e3b11d42dc162ee
元コミット内容
commit e4ae30f5f55b6b6ba2cb9a0c0e3b11d42dc162ee
Author: Russ Cox <rsc@golang.org>
Date: Tue Nov 1 21:46:59 2011 -0400
errors: new package
The only function is errors.New, at least for now.
R=r, gustavo, adg, r
CC=golang-dev
https://golang.org/cl/5321061
変更の背景
Go言語では、エラーは戻り値として扱われることが一般的であり、error
という組み込みインターフェースによって表現されます。このインターフェースは、Error() string
というメソッドを一つだけ持ち、エラーの詳細を文字列として返すことを期待します。
このコミット以前は、開発者がカスタムエラーを定義する際には、error
インターフェースを実装する独自の型を定義する必要がありました。しかし、単純なエラーメッセージを返すだけでよい場合でも、毎回新しい型を定義するのは冗長であり、共通のユーティリティ関数が求められていました。
errors
パッケージの導入、特にerrors.New
関数の提供は、この問題を解決するために行われました。これにより、開発者は簡潔にエラーメッセージを含むerror
型の値を生成できるようになり、Go言語におけるエラーハンドリングの基本的なパターンが確立されました。これは、Go言語のエラーハンドリング哲学である「エラーは値である」という考え方を具現化するものです。
前提知識の解説
Go言語におけるエラーハンドリング
Go言語では、例外処理のメカニズム(try-catch
など)は存在せず、エラーは関数の戻り値として明示的に扱われます。慣例として、関数は通常の結果とerror
型の戻り値をペアで返します。error
がnil
であれば成功、nil
でなければエラーが発生したことを意味します。
func doSomething() (resultType, error) {
// ... 処理 ...
if somethingWentWrong {
return zeroValue, errors.New("何らかのエラーが発生しました")
}
return actualResult, nil
}
error
インターフェース
Go言語のerror
は、以下のように定義された組み込みインターフェースです。
type error interface {
Error() string
}
このインターフェースを実装する任意の型は、error
型として扱われます。Error()
メソッドは、エラーに関する人間が読める形式の文字列を返します。
errors.New
の必要性
errors.New
関数が導入される前は、単純なエラーメッセージを返すためだけに、以下のようなカスタムエラー型を定義する必要がありました。
type myError string
func (e myError) Error() string {
return string(e)
}
func someFunction() error {
return myError("これはカスタムエラーです")
}
errors.New
は、このような定型的なコードを不要にし、より簡潔にエラーを生成する手段を提供します。
技術的詳細
errors
パッケージは、error
インターフェースを実装するerrorString
という非公開の構造体を定義し、そのインスタンスを返すNew
関数を提供します。
errorString
構造体
errorString
は、エラーメッセージを保持するためのシンプルな構造体です。
type errorString struct {
s string
}
この構造体は、error
インターフェースのError()
メソッドを実装しています。
func (e *errorString) Error() string {
return e.s
}
これにより、errorString
型のポインタ(*errorString
)はerror
インターフェースを満たします。
errors.New
関数
errors.New
関数は、引数として受け取った文字列をerrorString
構造体のs
フィールドに格納し、そのポインタをerror
型として返します。
func New(text string) error {
return &errorString{text}
}
この設計により、errors.New("some error")
と呼び出すだけで、"some error"
というメッセージを持つerror
型の値が生成されます。
TestNewEqual
の意図
errors_test.go
に含まれるTestNewEqual
テストは、errors.New
が呼び出されるたびに新しいerror
インスタンスを生成することを確認しています。
if New("abc") == New("abc") {
t.Errorf(`New("abc") == New("abc")`)
}
このテストは、同じ文字列を引数にしても、New
関数が異なるメモリアドレスを持つ新しいerrorString
インスタンスを返すため、それらが等しくないことを検証しています。これは、Go言語における値の比較の基本的な挙動と、errors.New
が新しいエラーオブジェクトを生成するという設計意図を示しています。エラーは値として扱われますが、その同一性はポインタの比較によって決まるため、異なる呼び出しで生成されたエラーはたとえメッセージが同じでも異なるオブジェクトとして扱われます。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが新規作成または変更されました。
src/pkg/Makefile
errors
パッケージをビルドシステムに追加するために、DIRS
変数にerrors
が追加されました。
--- a/src/pkg/Makefile
+++ b/src/pkg/Makefile
@@ -75,6 +75,7 @@ DIRS=\
encoding/git85\
encoding/hex\
encoding/pem\
+ errors\
exec\
exp/ebnf\
exp/ebnflint\
src/pkg/errors/Makefile
(新規作成)
errors
パッケージのビルド設定ファイルです。errors.go
をコンパイル対象として指定しています。
--- /dev/null
+++ b/src/pkg/errors/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../Make.inc
+
+TARG=errors
+GOFILES=\
+ errors.go\
+
+include ../../Make.pkg
src/pkg/errors/errors.go
(新規作成)
errors
パッケージの本体となるファイルです。errorString
型とNew
関数が定義されています。
--- /dev/null
+++ b/src/pkg/errors/errors.go
@@ -0,0 +1,20 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package errors implements functions to manipulate errors.
+package errors
+
+// New returns an error that formats as the given text.
+func New(text string) error {
+ return &errorString{text}
+}
+
+// errorString is a trivial implementation of error.
+type errorString struct {
+ s string
+}
+
+func (e *errorString) Error() string {
+ return e.s
+}
src/pkg/errors/errors_test.go
(新規作成)
errors
パッケージのテストファイルです。New
関数の挙動とError()
メソッドの動作を検証しています。
--- /dev/null
+++ b/src/pkg/errors/errors_test.go
@@ -0,0 +1,33 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package errors_test
+
+import (
+ . "errors"
+ "testing"
+)
+
+func TestNewEqual(t *testing.T) {
+ // Different allocations should not be equal.
+ if New("abc") == New("abc") {
+ t.Errorf(`New("abc") == New("abc")`)
+ }
+ if New("abc") == New("xyz") {
+ t.Errorf(`New("abc") == New("xyz")`)
+ }
+
+ // Same allocation should be equal to itself (not crash).
+ err := New("jkl")
+ if err != err {
+ t.Errorf(`err != err`)
+ }
+}
+
+func TestErrorMethod(t *t.T) {
+ err := New("abc")
+ if err.Error() != "abc" {
+ t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc")
+ }
+}
コアとなるコードの解説
src/pkg/errors/errors.go
このファイルはerrors
パッケージの心臓部です。
package errors
// New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
package errors
: このファイルがerrors
パッケージに属することを示します。func New(text string) error
:New
関数は、text
という文字列を引数に取り、error
インターフェース型を返します。return &errorString{text}
: ここが最も重要な部分です。errorString
という構造体の新しいインスタンスを生成し、そのs
フィールドに引数で渡されたtext
を格納します。そして、そのインスタンスへのポインタを返します。*errorString
型はError()
メソッドを実装しているため、error
インターフェースを満たします。
type errorString struct { s string }
:errorString
は、エラーメッセージ(文字列s
)を保持するためのシンプルな構造体です。この構造体はパッケージ外からは直接アクセスできない(小文字で始まるため)非公開の型です。
func (e *errorString) Error() string
:- これは
errorString
型がerror
インターフェースを実装するためのメソッドです。 e.s
、つまりerrorString
インスタンスが保持する文字列をそのまま返します。これにより、error
インターフェースの契約が満たされます。
- これは
src/pkg/errors/errors_test.go
このテストファイルは、errors
パッケージの基本的な機能が正しく動作することを確認します。
package errors_test
import (
. "errors"
"testing"
)
func TestNewEqual(t *testing.T) {
// Different allocations should not be equal.
if New("abc") == New("abc") {
t.Errorf(`New("abc") == New("abc")`)
}
if New("abc") == New("xyz") {
t.Errorf(`New("abc") == New("abc")`)
}
// Same allocation should be equal to itself (not crash).
err := New("jkl")
if err != err {
t.Errorf(`err != err`)
}
}
func TestErrorMethod(t *testing.T) {
err := New("abc")
if err.Error() != "abc" {
t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc")
}
}
import . "errors"
:errors
パッケージをインポートし、そのエクスポートされた識別子(この場合はNew
関数)をパッケージ名なしで直接使用できるようにします。テストコードでよく見られるパターンです。TestNewEqual
:New("abc") == New("abc")
の比較がtrue
にならないことを検証しています。これは、New
関数が呼び出されるたびに新しい*errorString
インスタンスがヒープに割り当てられるため、たとえ同じ文字列を渡しても、異なるメモリアドレスを持つオブジェクトが生成されることを確認しています。Go言語では、インターフェース値の比較は、その内部の動的な型と値(ポインタの場合はポインタ値)が等しいかどうかで行われます。New("abc") == New("xyz")
も同様にfalse
になることを検証しています。err := New("jkl"); if err != err
は、同じ変数に対する比較が常にtrue
であることを確認しており、これは基本的な健全性チェックです。
TestErrorMethod
:New("abc").Error()
が期待通り"abc"
という文字列を返すことを検証しています。これは、errorString
型がError()
メソッドを正しく実装していることを確認するものです。
これらのコードは、Go言語におけるエラーハンドリングの基本的な構成要素を非常にシンプルかつ効果的に提供しています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/e4ae30f5f55b6b6ba2cb9a0c0e3b11d42dc162ee
- Go Code Review (CL) ページ: https://golang.org/cl/5321061
参考にした情報源リンク
- Go言語公式ドキュメント - Errors: https://go.dev/blog/error-handling-and-go (Go言語のエラーハンドリングに関する公式ブログ記事)
- Go言語公式ドキュメント - The Go Programming Language Specification (Errors): https://go.dev/ref/spec#Errors (Go言語仕様におけるエラーの定義)
- Go言語公式ドキュメント - errorsパッケージ: https://pkg.go.dev/errors (errorsパッケージの公式ドキュメント)
- Go言語公式ドキュメント - Effective Go (Errors): https://go.dev/doc/effective_go#errors (Effective Goにおけるエラーハンドリングのベストプラクティス)