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

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

このコミットは、Go言語の標準ライブラリであるcryptoパッケージ内のエラー変数名の命名規則を統一し、よりGoらしい慣習に合わせるための変更です。具体的には、FooErrorという形式のエラー変数をErrFooという形式にリネームし、エクスポートされていない関数が返すエラー型を明示的に追加しています。

変更されたファイルは以下の通りです。

  • src/pkg/crypto/dsa/dsa.go
  • src/pkg/crypto/openpgp/errors/errors.go
  • src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go
  • src/pkg/crypto/openpgp/read.go
  • src/pkg/crypto/openpgp/read_test.go

コミット

commit da6d835b90a52d9f86aaf526903fb491b7bb41a6
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Tue Jan 24 08:32:43 2012 -0800

    crypto: rename some FooError to ErrFoo
    
    Also, add an explicit error type when the right hand side is an unexported
    function.
    
    R=golang-dev, gri, rogpeppe, agl, rsc
    CC=golang-dev
    https://golang.org/cl/5564048

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

https://github.com/golang/go/commit/da6d835b90a52d9f86aaf526903fb491b7bb41a6

元コミット内容

crypto: rename some FooError to ErrFoo

Also, add an explicit error type when the right hand side is an unexported function.

変更の背景

このコミットの主な背景は、Go言語におけるエラーの命名規則の標準化と、コードの可読性および一貫性の向上にあります。Go言語では、慣習的にエクスポートされるエラー変数はErrプレフィックスを付けて命名されます(例: io.EOFos.ErrPermission)。これは、エラーが特定の条件を示す値であることを明確にし、他の変数と区別しやすくするためです。

コミットが行われた2012年当時、Go言語はまだ比較的新しく、ライブラリ全体で命名規則が完全に統一されていない部分がありました。cryptoパッケージも例外ではなく、InvalidPublicKeyErrorKeyIncorrectErrorといった形式のエラー変数が存在していました。これらの命名は、エラーが型であるかのように誤解される可能性があり、Goの慣習とは異なっていました。

このコミットは、これらのエラー変数をErrInvalidPublicKeyErrKeyIncorrectのようにErrプレフィックスを持つ形式にリネームすることで、Goの標準的なエラー命名規則に準拠させ、コードベース全体の一貫性を高めることを目的としています。また、エクスポートされていない(アンエクスポートされた)関数がエラーを返す場合に、そのエラー型を明示的に定義することで、より堅牢なエラーハンドリングを可能にしています。

前提知識の解説

Go言語のエラーハンドリング

Go言語では、エラーは組み込みのerrorインターフェースを実装する値として扱われます。関数は通常、最後の戻り値としてerror型を返します。エラーが発生しなかった場合はnilを返します。

func doSomething() (result string, err error) {
    // ... 処理 ...
    if someCondition {
        return "", errors.New("something went wrong") // エラーを返す
    }
    return "success", nil // 成功を返す
}

エラーの命名規則 (Errプレフィックス)

Goの公式ドキュメントやEffective Goなどのスタイルガイドでは、エクスポートされるエラー変数の命名について特定の慣習が推奨されています。それは、エラーを示す変数にはErrプレフィックスを付けるというものです。

例:

  • io.EOF (End Of File)
  • os.ErrPermission (Permission denied)
  • fmt.Errorf (エラー文字列から新しいエラーを作成する関数)

この慣習は、以下の理由から重要です。

  1. 明確性: 変数がエラー値であることを一目で識別できます。
  2. 一貫性: 標準ライブラリ全体で統一された命名規則に従うことで、コードベース全体の理解が容易になります。
  3. 誤解の防止: FooErrorのような命名は、FooErrorがエラー型であるかのように誤解される可能性があります。しかし、Goではエラーは通常、errorインターフェースを実装する具体的な型(構造体など)のインスタンスとして定義され、そのインスタンスが変数としてエクスポートされます。ErrFooという命名は、それがエラー値であることを明確に示します。

アンエクスポートされた型と関数

Go言語では、識別子(変数、関数、型など)の最初の文字が大文字である場合、それはエクスポートされ、パッケージ外からアクセス可能です。最初の文字が小文字である場合、それはアンエクスポートされ、そのパッケージ内でのみアクセス可能です。

このコミットでは、「右辺がアンエクスポートされた関数である場合に明示的なエラー型を追加する」とあります。これは、例えばvar MyError = myUnexportedFunc()のような場合、MyErrorがエクスポートされていても、その値がアンエクスポートされた関数によって生成されるため、そのエラーの具体的な型が外部から見えにくい、あるいは意図しない依存関係を生む可能性があることを指します。明示的なエラー型を定義することで、この問題を解決し、エラーの型チェックなどをより堅牢に行えるようにします。

技術的詳細

このコミットの技術的な変更は、主に以下の2点に集約されます。

  1. エラー変数のリネーム:

    • src/pkg/crypto/dsa/dsa.go内のInvalidPublicKeyErrorErrInvalidPublicKeyにリネームされました。
    • src/pkg/crypto/openpgp/errors/errors.go内のKeyIncorrectErrorErrKeyIncorrectに、UnknownIssuerErrorErrUnknownIssuerにリネームされました。
    • これらのリネームに伴い、関連するコード箇所(src/pkg/crypto/dsa/dsa.gosrc/pkg/crypto/openpgp/packet/symmetrically_encrypted.gosrc/pkg/crypto/openpgp/read.gosrc/pkg/crypto/openpgp/read_test.go)で、リネームされたエラー変数の参照も更新されています。

    この変更は、前述のGoのエラー命名慣習に準拠するためのものです。FooErrorという命名は、FooErrorがエラー型であるかのように見えますが、実際にはerrorインターフェースを実装する特定の型のインスタンス(値)です。ErrFooとすることで、それがエラー値であることを明確に示し、コードの意図をより正確に伝えます。

  2. アンエクスポートされた関数が返すエラーの明示的な型定義: コミットメッセージには「Also, add an explicit error type when the right hand side is an unexported function.」とありますが、提供されたdiffからは、直接的に新しいアンエクスポートされたエラー型が追加された箇所は読み取れません。しかし、var ErrInvalidPublicKey error = invalidPublicKeyError(0)のように、エクスポートされたエラー変数に、アンエクスポートされた型invalidPublicKeyErrorのインスタンスを明示的にerror型として代入している箇所が見られます。

    これは、invalidPublicKeyErrorのようなアンエクスポートされた具体的なエラー型を定義し、そのインスタンスをエクスポートされたerror型の変数に割り当てるというGoの一般的なパターンです。これにより、パッケージ内部では具体的なエラー型を使って詳細なエラーハンドリングを行うことができ、パッケージ外部にはerrorインターフェースとして抽象化されたエラーを提供できます。このパターンは、エラーの内部実装を隠蔽しつつ、必要に応じてerrors.Iserrors.As(Go 1.13以降)のような関数を使ってエラーの具体的な型をチェックすることを可能にします。

    このコミットの時点(Go 1.x初期)では、errors.Iserrors.Asは存在しませんでしたが、エラー値を比較する際にはerr == ErrFooのように直接比較することが一般的でした。そのため、エクスポートされたエラー変数がerrorインターフェース型を持つことで、その比較がより自然に行えるようになります。

これらの変更は、Go言語の進化の初期段階において、標準ライブラリのコード品質と一貫性を向上させるための重要なステップでした。

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

src/pkg/crypto/dsa/dsa.go

--- a/src/pkg/crypto/dsa/dsa.go
+++ b/src/pkg/crypto/dsa/dsa.go
@@ -35,11 +35,11 @@ func (invalidPublicKeyError) Error() string {
 	return "crypto/dsa: invalid public key"
 }
 
-// InvalidPublicKeyError results when a public key is not usable by this code.
+// ErrInvalidPublicKey results when a public key is not usable by this code.
 // FIPS is quite strict about the format of DSA keys, but other code may be
 // less so. Thus, when using keys which may have been generated by other code,
 // this error must be handled.
-var InvalidPublicKeyError = invalidPublicKeyError(0)
+var ErrInvalidPublicKey error = invalidPublicKeyError(0)
 
 // ParameterSizes is a enumeration of the acceptable bit lengths of the primes
 // in a set of DSA parameters. See FIPS 186-3, section 4.2.
@@ -194,7 +194,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
 
 	n := priv.Q.BitLen()
 	if n&7 != 0 {
-		err = InvalidPublicKeyError
+		err = ErrInvalidPublicKey
 		return
 	}
 	n >>= 3

src/pkg/crypto/openpgp/errors/errors.go

--- a/src/pkg/crypto/openpgp/errors/errors.go
+++ b/src/pkg/crypto/openpgp/errors/errors.go
@@ -47,7 +47,7 @@ func (ki keyIncorrectError) Error() string {
 	return "the given key was incorrect"
 }
 
-var KeyIncorrectError = keyIncorrectError(0)
+var ErrKeyIncorrect error = keyIncorrectError(0)
 
 type unknownIssuerError int
 
@@ -55,7 +55,7 @@ func (unknownIssuerError) Error() string {
 	return "signature make by unknown entity"
 }
 
-var UnknownIssuerError = unknownIssuerError(0)
+var ErrUnknownIssuer error = unknownIssuerError(0)
 
 type UnknownPacketTypeError uint8

コアとなるコードの解説

上記のコード変更箇所は、Goのエラー命名規則への準拠と、エラー変数の型定義の明確化を示しています。

  1. InvalidPublicKeyError から ErrInvalidPublicKey へのリネーム:

    • 元のコードでは var InvalidPublicKeyError = invalidPublicKeyError(0) となっていました。ここで invalidPublicKeyError はアンエクスポートされた(小文字で始まる)型です。InvalidPublicKeyError という変数名は大文字で始まるためエクスポートされますが、その命名は「エラー型」のように見えてしまいます。
    • 変更後、var ErrInvalidPublicKey error = invalidPublicKeyError(0) となりました。
      • 変数名が ErrInvalidPublicKey となり、Goの慣習に従いエラー値であることを明確に示しています。
      • error = の部分が追加され、ErrInvalidPublicKey が明示的にerrorインターフェース型であることを宣言しています。これにより、invalidPublicKeyError(0)という具体的な型の値がerrorインターフェースに適合することを示し、コードの意図がより明確になります。これは、Goのインターフェースの暗黙的な実装(invalidPublicKeyErrorError() stringメソッドを持つためerrorインターフェースを満たす)を利用しつつ、エクスポートされる変数の型を明示的にerrorとすることで、外部からの利用者がその変数をerrorとして扱うことを促します。
    • このリネームに伴い、Sign関数内でエラーを返す箇所も err = InvalidPublicKeyError から err = ErrInvalidPublicKey に変更されています。
  2. KeyIncorrectError から ErrKeyIncorrectUnknownIssuerError から ErrUnknownIssuer へのリネーム:

    • src/pkg/crypto/openpgp/errors/errors.go でも同様に、KeyIncorrectErrorUnknownIssuerError がそれぞれ ErrKeyIncorrectErrUnknownIssuer にリネームされ、errorインターフェース型が明示的に宣言されています。
    • これらのエラー変数が使用されている他のファイル(src/pkg/crypto/openpgp/packet/symmetrically_encrypted.gosrc/pkg/crypto/openpgp/read.gosrc/pkg/crypto/openpgp/read_test.go)でも、対応する参照が更新されています。

これらの変更は、Go言語のコードベース全体におけるエラーハンドリングの一貫性と可読性を高めるための、クリーンアップ作業の一環です。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント
  • Effective Go
  • Go Code Review Comments (Go Wiki)
  • GitHubのコミット履歴とdiff
  • Go言語のエラーハンドリングに関する一般的な知識