Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10196] ファイルの概要

このコミットは、Go言語の標準ライブラリ全体で、エラー型 os.Error を組み込みの error インターフェースにリネームする変更を反映したものです。これは、Go 1のリリースに向けた重要な変更の一環であり、エラーハンドリングの統一と簡素化を目的としています。ドキュメント、ツール、および様々なパッケージ内のコードがこの新しいエラーモデルに適合するように更新されています。

コミット

  • コミットハッシュ: 492098eb759bba2ff5c86b0a868158afe32e91f8
  • Author: Russ Cox rsc@golang.org
  • Date: Tue Nov 1 22:58:09 2011 -0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/492098eb759bba2ff5c86b0a868158afe32e91f8

元コミット内容

all: rename os.Error to error in various non-code contexts

R=adg
CC=golang-dev
https://golang.org/cl/5328062

変更の背景

Go言語の初期バージョンでは、エラーを表すために os.Error という具体的な型が使用されていました。しかし、Go 1のリリースに向けて、エラーハンドリングのメカニズムをより柔軟で統一されたものにする必要性が認識されました。

この変更の主な背景は以下の通りです。

  1. 統一されたエラーインターフェースの導入: os.Erroros パッケージに属する具体的な型でしたが、エラーは言語の基本的な概念であり、特定のパッケージに限定されるべきではありません。そこで、Go言語の設計者は、error という名前の組み込みインターフェースを導入することを決定しました。これにより、どのような型でも Error() string メソッドを実装していれば error インターフェースを満たすことができ、より多様なエラー表現が可能になります。
  2. エラーハンドリングの簡素化と一貫性: error インターフェースの導入により、関数は具体的なエラー型ではなく、汎用的な error インターフェースを返すことができるようになりました。これにより、呼び出し側は特定のエラー型に依存することなく、一貫した方法でエラーを処理できるようになります。
  3. 言語の成熟: Go 1はGo言語の最初の安定版リリースであり、言語の設計と標準ライブラリのAPIを最終決定する重要な時期でした。この os.Error から error への変更は、言語の成熟度を高め、将来の拡張性を確保するための重要なステップでした。

このコミットは、この大規模な変更の一環として、コード以外のコンテキスト(ドキュメント、コメント、テストの期待値など)における os.Error の参照を error に更新するものです。

前提知識の解説

os.Error (Go 1以前)

Go 1以前のGo言語では、エラーは os パッケージで定義された os.Error という具体的な型によって表現されていました。これは、エラーメッセージを保持するためのシンプルな構造体でした。

// Go 1以前の概念的なos.Errorの定義
package os

type Error interface {
    String() string
}

type ErrorString string

func (e ErrorString) String() string {
    return string(e)
}

func NewError(s string) Error {
    return ErrorString(s)
}

関数がエラーを返す場合、その戻り値の型は os.Error となっていました。

error インターフェース (Go 1以降)

Go 1以降、os.Error は廃止され、Go言語に組み込みの error インターフェースが導入されました。このインターフェースは非常にシンプルで、Error() string という単一のメソッドのみを定義しています。

// Go言語の組み込みerrorインターフェース
type error interface {
    Error() string
}

この設計の重要な点は、任意の型が Error() string メソッドを実装していれば、暗黙的に error インターフェースを満たすという点です。これにより、開発者は独自のエラー型を定義し、それを error として返すことができるようになり、エラーに付加情報を持たせたり、特定のエラーを型アサーションで識別したりすることが可能になりました。

errors パッケージと errors.New

os.Error の廃止に伴い、新しい errors パッケージが導入されました。このパッケージには、シンプルなエラーを作成するための errors.New 関数が含まれています。

package errors

func New(text string) error {
    return &errorString{text}
}

type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

errors.New("some error message") は、指定された文字列を返す Error() メソッドを持つ error 型の値を返します。

go fix ツール

Go言語には、古いAPIの使用を新しいAPIに自動的に変換する go fix というツールがあります。os.Error から error への移行のような大規模な変更では、go fix が多くのコードを自動的に更新するのに役立ちました。しかし、コメントやドキュメント内の参照など、コードのセマンティクスに直接影響しない箇所は手動での修正が必要となる場合がありました。

技術的詳細

このコミットは、Go言語のエラーハンドリングモデルの根本的な変更を反映しています。

  1. インターフェースとしてのエラー: 最も重要な変更は、エラーが具体的な型 (os.Error) からインターフェース (error) になったことです。これにより、Goのエラーハンドリングはより柔軟で、Goのインターフェースの強力な特性を活かせるようになりました。開発者は、エラーにコンテキスト情報(例えば、エラーコード、スタックトレース、追加の詳細データ)を含めるために、カスタムエラー型を定義できるようになりました。
  2. Error() string メソッド: error インターフェースは Error() string メソッドを要求します。これは、エラーを人間が読める文字列形式で表現するための標準的な方法を提供します。これにより、エラーログの出力やユーザーへのエラーメッセージ表示が一貫して行えます。
  3. nil の意味: Goでは、nil はエラーがないことを示します。これは os.Error 時代から変わっていませんが、error インターフェースになったことで、カスタムエラー型が nil ポインタとして返される場合でも、それが nil と比較されると nil と評価されるというGoのインターフェースの特性が重要になります。
  4. os.ErrorString から errors.New への移行: os.ErrorStringos.Error を実装する具体的な文字列型でした。このコミットでは、rpc パッケージのドキュメントで os.ErrorStringerrors.New によって作成されたかのようにクライアントに見える、という説明に変わっています。これは、エラーの生成方法が errors パッケージに集約されたことを示しています。
  5. ドキュメントとコメントの更新: このコミットの大部分は、コードの動作そのものを変更するのではなく、ドキュメント、コメント、テストの期待される出力など、コード以外の部分で os.Error という文字列を error に置き換えることに焦点を当てています。これは、新しいエラーモデルへの移行を完全に反映し、開発者が古い概念に混乱しないようにするための重要な作業です。例えば、doc/codelab/wiki/index.htmldoc/debugging_with_gdb.html のようなドキュメントファイルが更新されています。
  6. govet の更新: src/cmd/govet/govet.go の変更は、govet ツールがGoのコードを静的に分析する際に、新しい error インターフェースのシグネチャを正しく認識するように更新されたことを示しています。特に、標準的なインターフェースメソッド(GobDecode, MarshalJSON, ReadByte など)の戻り値の型が os.Error から error に変更されています。これにより、govet はGo 1のエラーハンドリング規約に準拠しているかをチェックできるようになります。

コアとなるコードの変更箇所

このコミットは広範囲にわたる変更を含んでいますが、特に重要な変更箇所をいくつか抜粋して解説します。

1. ドキュメントの更新 (doc/codelab/wiki/index.html)

--- a/doc/codelab/wiki/index.html
+++ b/doc/codelab/wiki/index.html
@@ -107,7 +107,7 @@ func (p *Page) save() error {
 <p>
 This method's signature reads: "This is a method named <code>save</code> that
 takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
--no parameters, and returns a value of type <code>os.Error</code>." 
+-no parameters, and returns a value of type <code>error</code>." 
 </p>
 
 <p>
@@ -116,7 +116,7 @@ file. For simplicity, we will use the <code>Title</code> as the file name.
 </p>
 
 <p>
--The <code>save</code> method returns an <code>os.Error</code> value because
-+The <code>save</code> method returns an <code>error</code> value because
 that is the return type of <code>WriteFile</code> (a standard library function
 that writes a byte slice to a file).  The <code>save</code> method returns the
 error value, to let the application handle it should anything go wrong while
@@ -152,7 +152,7 @@ The function <code>loadPage</code> constructs the file name from
 
 <p>
 Functions can return multiple values. The standard library function 
--<code>io.ReadFile</code> returns <code>[]byte</code> and <code>os.Error</code>. 
-+<code>io.ReadFile</code> returns <code>[]byte</code> and <code>error</code>. 
 In <code>loadPage</code>, error isn't being handled yet; the "blank identifier"
 represented by the underscore (<code>_</code>) symbol is used to throw away the
 error return value (in essence, assigning the value to nothing). 
@@ -161,7 +161,7 @@ error return value (in essence, assigning the value to nothing).
 <p>
 But what happens if <code>ReadFile</code> encounters an error?  For example,
 the file might not exist. We should not ignore such errors.  Let's modify the
--function to return <code>*Page</code> and <code>os.Error</code>.
-+function to return <code>*Page</code> and <code>error</code>.
 </p>

2. govet ツールのメソッドシグネチャ定義の更新 (src/cmd/govet/govet.go)

--- a/src/cmd/govet/govet.go
+++ b/src/cmd/govet/govet.go
@@ -232,23 +232,23 @@ type MethodSig struct {
 // we let it go.  But if it does have a fmt.ScanState, then the
 // rest has to match.\nvar canonicalMethods = map[string]MethodSig{
-	// "Flush": {{}, {"os.Error"}}, // http.Flusher and jpeg.writer conflict
-	"Format":        {[]string{"=fmt.State", "rune"}, []string{}},               // fmt.Formatter
-	"GobDecode":     {[]string{"[]byte"}, []string{"os.Error"}},                 // gob.GobDecoder
-	"GobEncode":     {[]string{}, []string{"[]byte", "os.Error"}},               // gob.GobEncoder
-	"MarshalJSON":   {[]string{}, []string{"[]byte", "os.Error"}},               // json.Marshaler
-	"MarshalXML":    {[]string{}, []string{"[]byte", "os.Error"}},               // xml.Marshaler
-	"Peek":          {[]string{"=int"}, []string{"[]byte", "os.Error"}},         // image.reader (matching bufio.Reader)
-	"ReadByte":      {[]string{}, []string{"byte", "os.Error"}},                 // io.ByteReader
-	"ReadFrom":      {[]string{"=io.Reader"}, []string{"int64", "os.Error"}},    // io.ReaderFrom
-	"ReadRune":      {[]string{}, []string{"rune", "int", "os.Error"}},          // io.RuneReader
-	"Scan":          {[]string{"=fmt.ScanState", "rune"}, []string{"os.Error"}}, // fmt.Scanner
-	"Seek":          {[]string{"=int64", "int"}, []string{"int64", "os.Error"}}, // io.Seeker
-	"UnmarshalJSON": {[]string{"[]byte"}, []string{"os.Error"}},                 // json.Unmarshaler
-	"UnreadByte":    {[]string{}, []string{"os.Error"}},
-	"UnreadRune":    {[]string{}, []string{"os.Error"}},
-	"WriteByte":     {[]string{"byte"}, []string{"os.Error"}},                // jpeg.writer (matching bufio.Writer)
-	"WriteTo":       {[]string{"=io.Writer"}, []string{"int64", "os.Error"}}, // io.WriterTo
+	// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
+	"Format":        {[]string{"=fmt.State", "rune"}, []string{}},            // fmt.Formatter
+	"GobDecode":     {[]string{"[]byte"}, []string{"error"}},                 // gob.GobDecoder
+	"GobEncode":     {[]string{}, []string{"[]byte", "error"}},               // gob.GobEncoder
+	"MarshalJSON":   {[]string{}, []string{"[]byte", "error"}},               // json.Marshaler
+	"MarshalXML":    {[]string{}, []string{"[]byte", "error"}},               // xml.Marshaler
+	"Peek":          {[]string{"=int"}, []string{"[]byte", "error"}},         // image.reader (matching bufio.Reader)
+	"ReadByte":      {[]string{}, []string{"byte", "error"}},                 // io.ByteReader
+	"ReadFrom":      {[]string{"=io.Reader"}, []string{"int64", "error"}},    // io.ReaderFrom
+	"ReadRune":      {[]string{}, []string{"rune", "int", "error"}},          // io.RuneReader
+	"Scan":          {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner
+	"Seek":          {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker
+	"UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}},                 // json.Unmarshaler
+	"UnreadByte":    {[]string{}, []string{"error"}},
+	"UnreadRune":    {[]string{}, []string{"error"}},
+	"WriteByte":     {[]string{"byte"}, []string{"error"}},                // jpeg.writer (matching bufio.Writer)
+	"WriteTo":       {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo
 }

3. rpc パッケージのドキュメントと型情報の更新 (src/pkg/rpc/server.go)

--- a/src/pkg/rpc/server.go
+++ b/src/pkg/rpc/server.go
@@ -18,12 +18,12 @@
 		  registering the service).
 		- the method has two arguments, both exported or local types.
 		- the method's second argument is a pointer.
--		- the method has return type os.Error.
+-		- the method has return type error.
 
 	The method's first argument represents the arguments provided by the caller; the
 	second argument represents the result parameters to be returned to the caller.
 	The method's return value, if non-nil, is passed back as a string that the client
--	sees as an os.ErrorString.
+-	sees as if created by errors.New.
 
 	The server may handle requests on a single connection by calling ServeConn.  More
 	typically it will create a network listener and call Accept or, for an HTTP
@@ -55,14 +55,14 @@
 
 		type Arith int
 
-		func (t *Arith) Multiply(args *Args, reply *int) os.Error {
+		func (t *Arith) Multiply(args *Args, reply *int) error {
 			*reply = args.A * args.B
 			return nil
 		}
 
-		func (t *Arith) Divide(args *Args, quo *Quotient) os.Error {
+		func (t *Arith) Divide(args *Args, quo *Quotient) error {
 			if args.B == 0 {
-				return os.ErrorString("divide by zero")
+				return errors.New("divide by zero")
 			}
 			quo.Quo = args.A / args.B
 			quo.Rem = args.A % args.B
@@ -133,10 +133,9 @@ const (
 	DefaultDebugPath = "/debug/rpc"
 )
 
-// Precompute the reflect type for os.Error.  Can't use os.Error directly
+// Precompute the reflect type for error.  Can't use error directly
 // because Typeof takes an empty interface value.  This is annoying.
-var unusedError *error
-var typeOfOsError = reflect.TypeOf(unusedError).Elem()
+var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
 
 type methodType struct {
 	sync.Mutex // protects counters
@@ -210,13 +209,13 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
 // receiver value that satisfy the following conditions:
 //	- exported method
 //	- two arguments, both pointers to exported structs
-//	- one return value, of type os.Error
+//	- one return value, of type error
 // It returns an error if the receiver is not an exported type or has no
 // suitable methods.
 // The client accesses each method using a string of the form "Type.Method",
@@ -281,13 +280,13 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
 			log.Println("method", mname, "reply type not exported or local:", replyType)
 			continue
 		}
-		// Method needs one out: os.Error.
+		// Method needs one out: error.
 		if mtype.NumOut() != 1 {
 			log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
 			continue
 		}
-		if returnType := mtype.Out(0); returnType != typeOfOsError {
-			log.Println("method", mname, "returns", returnType.String(), "not os.Error")
+		if returnType := mtype.Out(0); returnType != typeOfError {
+			log.Println("method", mname, "returns", returnType.String(), "not error")
 			continue
 		}
 		s.method[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
@@ -339,7 +338,7 @@ func (s *service) call(server *Server, sending *sync.Mutex, mtype *methodType, r
 	function := mtype.method.Func
 	// Invoke the method, providing a new value for the reply.
 	returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv})
-	// The return value for the method is an os.Error.
+	// The return value for the method is an error.
 	errInter := returnValues[0].Interface()
 	errmsg := ""
 	if errInter != nil {

4. utf8 パッケージのエラー定義の更新 (src/pkg/utf8/string.go)

--- a/src/pkg/utf8/string.go
+++ b/src/pkg/utf8/string.go
@@ -4,6 +4,8 @@
 
 package utf8
 
+import "errors"
+
 // String wraps a regular string with a small structure that provides more
 // efficient indexing by code point index, as opposed to byte index.
 // Scanning incrementally forwards or backwards is O(1) per index operation
@@ -193,19 +195,5 @@ func (s *String) At(i int) rune {
 	return r
 }
 
-// We want the panic in At(i) to satisfy os.Error, because that's what
-// runtime panics satisfy, but we can't import os.  This is our solution.\n
-// error is the type of the error returned if a user calls String.At(i) with i out of range.
-// It satisfies os.Error and runtime.Error.
-type error_ string
-\n-func (err error_) String() string {
-	return string(err)
-}
-\n-func (err error_) RunTimeError() {
-}
-\n-var outOfRange = error_("utf8.String: index out of range")
-var sliceOutOfRange = error_("utf8.String: slice index out of range")
+var outOfRange = errors.New("utf8.String: index out of range")
+var sliceOutOfRange = errors.New("utf8.String: slice index out of range")

コアとなるコードの解説

1. ドキュメントの更新 (doc/codelab/wiki/index.html)

この変更は、Goのチュートリアルやドキュメントが新しいエラーモデルを反映するように更新されたことを示しています。以前は os.Error と明示的に記述されていた箇所が、すべて error に置き換えられています。

  • func (p *Page) save() error { の戻り値の型が os.Error から error に変更されたことを説明するテキスト。
  • save メソッドが os.Error を返す理由として WriteFile の戻り値が挙げられていた箇所が error に変更。
  • io.ReadFile[]byteos.Error を返すと説明されていた箇所が []byteerror を返すように変更。
  • loadPage 関数が *Pageos.Error を返すように変更されると説明されていた箇所が *Pageerror を返すように変更。

これらの変更は、Go言語の学習者が最初から正しいエラーハンドリングの概念に触れることができるようにするためのものです。

2. govet ツールのメソッドシグネチャ定義の更新 (src/cmd/govet/govet.go)

govet はGoのコードを静的に分析し、潜在的なバグや疑わしい構造を報告するツールです。この変更は、govet がGoの標準ライブラリで定義されている特定のインターフェースのメソッドシグネチャをチェックする際に使用する内部マップ canonicalMethods を更新しています。

以前は、GobDecode, MarshalJSON, ReadByte などのメソッドの戻り値の型として os.Error が期待されていました。このコミットでは、これらの期待される戻り値の型がすべて error に変更されています。

これにより、govet はGo 1以降の新しいエラーハンドリング規約に準拠しているかを正確にチェックできるようになり、開発者が古い os.Error を使用している場合に警告を発するなどの機能を提供できるようになります。

3. rpc パッケージのドキュメントと型情報の更新 (src/pkg/rpc/server.go)

rpc (Remote Procedure Call) パッケージは、ネットワーク経由で関数を呼び出すためのメカニズムを提供します。この変更は、rpc サービスのメソッドがエラーを返す際の規約を更新しています。

  • ドキュメントの更新: rpc メソッドの戻り値の型が os.Error から error に変更されたことが明記されています。また、クライアントが受け取るエラーが os.ErrorString ではなく、errors.New によって作成されたかのように見える、という説明に変わっています。これは、エラーの生成と伝播のメカニズムが errors パッケージに統一されたことを示唆しています。
  • リフレクション型情報の更新: rpc パッケージは、リフレクションを使用してサービスメソッドのシグネチャを検査します。以前は os.Error のリフレクション型 (typeOfOsError) を事前に計算していましたが、これが error インターフェースのリフレクション型 (typeOfError) に変更されています。
    • var unusedError *errorvar typeOfOsError = reflect.TypeOf(unusedError).Elem() の行が削除され、代わりに var typeOfError = reflect.TypeOf((*error)(nil)).Elem() が追加されています。これは、error がインターフェースであるため、reflect.TypeOf に直接渡すことができないため、(*error)(nil) のようにポインタ型を経由してその要素型 (Elem()) を取得するというGoのリフレクションのイディオムを使用しています。
  • メソッドシグネチャチェックの更新: register 関数内で、登録されるメソッドの戻り値の型が typeOfOsError ではなく typeOfError と比較されるように変更されています。これにより、rpc サーバーはGo 1のエラーハンドリング規約に準拠したメソッドのみを登録するようになります。

これらの変更により、rpc パッケージはGo 1のエラーモデルに完全に適合し、より現代的なエラーハンドリングをサポートするようになりました。

4. utf8 パッケージのエラー定義の更新 (src/pkg/utf8/string.go)

utf8 パッケージはUTF-8文字列の操作を提供します。この変更は、utf8.String 型の At メソッドが範囲外のインデックスでパニックを起こす際に使用するエラー型を更新しています。

以前は、os.Errorruntime.Error の両方を満たすように error_ というカスタム型が定義されていました。これは、os パッケージをインポートせずに os.Error を満たすための回避策でした。

このコミットでは、このカスタム error_ 型が削除され、代わりに標準の errors.New 関数を使用してエラーが作成されるようになりました。

  • import "errors" が追加されています。
  • error_ 型の定義と、その String() および RunTimeError() メソッドが削除されています。
  • outOfRangesliceOutOfRange の変数が、error_ 型のインスタンスではなく、errors.New によって作成された error 型のインスタンスを保持するように変更されています。

この変更は、Go 1で error インターフェースが組み込み型となり、errors パッケージが提供されたことで、このようなカスタムエラー型の定義が不要になったことを示しています。これにより、コードがよりシンプルになり、標準的なエラーハンドリングのプラクティスに準拠するようになりました。

関連リンク

参考にした情報源リンク