[インデックス 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.EOF
、os.ErrPermission
)。これは、エラーが特定の条件を示す値であることを明確にし、他の変数と区別しやすくするためです。
コミットが行われた2012年当時、Go言語はまだ比較的新しく、ライブラリ全体で命名規則が完全に統一されていない部分がありました。crypto
パッケージも例外ではなく、InvalidPublicKeyError
やKeyIncorrectError
といった形式のエラー変数が存在していました。これらの命名は、エラーが型であるかのように誤解される可能性があり、Goの慣習とは異なっていました。
このコミットは、これらのエラー変数をErrInvalidPublicKey
やErrKeyIncorrect
のように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
(エラー文字列から新しいエラーを作成する関数)
この慣習は、以下の理由から重要です。
- 明確性: 変数がエラー値であることを一目で識別できます。
- 一貫性: 標準ライブラリ全体で統一された命名規則に従うことで、コードベース全体の理解が容易になります。
- 誤解の防止:
FooError
のような命名は、FooError
がエラー型であるかのように誤解される可能性があります。しかし、Goではエラーは通常、error
インターフェースを実装する具体的な型(構造体など)のインスタンスとして定義され、そのインスタンスが変数としてエクスポートされます。ErrFoo
という命名は、それがエラー値であることを明確に示します。
アンエクスポートされた型と関数
Go言語では、識別子(変数、関数、型など)の最初の文字が大文字である場合、それはエクスポートされ、パッケージ外からアクセス可能です。最初の文字が小文字である場合、それはアンエクスポートされ、そのパッケージ内でのみアクセス可能です。
このコミットでは、「右辺がアンエクスポートされた関数である場合に明示的なエラー型を追加する」とあります。これは、例えばvar MyError = myUnexportedFunc()
のような場合、MyError
がエクスポートされていても、その値がアンエクスポートされた関数によって生成されるため、そのエラーの具体的な型が外部から見えにくい、あるいは意図しない依存関係を生む可能性があることを指します。明示的なエラー型を定義することで、この問題を解決し、エラーの型チェックなどをより堅牢に行えるようにします。
技術的詳細
このコミットの技術的な変更は、主に以下の2点に集約されます。
-
エラー変数のリネーム:
src/pkg/crypto/dsa/dsa.go
内のInvalidPublicKeyError
がErrInvalidPublicKey
にリネームされました。src/pkg/crypto/openpgp/errors/errors.go
内のKeyIncorrectError
がErrKeyIncorrect
に、UnknownIssuerError
がErrUnknownIssuer
にリネームされました。- これらのリネームに伴い、関連するコード箇所(
src/pkg/crypto/dsa/dsa.go
、src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go
、src/pkg/crypto/openpgp/read.go
、src/pkg/crypto/openpgp/read_test.go
)で、リネームされたエラー変数の参照も更新されています。
この変更は、前述のGoのエラー命名慣習に準拠するためのものです。
FooError
という命名は、FooError
がエラー型であるかのように見えますが、実際にはerror
インターフェースを実装する特定の型のインスタンス(値)です。ErrFoo
とすることで、それがエラー値であることを明確に示し、コードの意図をより正確に伝えます。 -
アンエクスポートされた関数が返すエラーの明示的な型定義: コミットメッセージには「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.Is
やerrors.As
(Go 1.13以降)のような関数を使ってエラーの具体的な型をチェックすることを可能にします。このコミットの時点(Go 1.x初期)では、
errors.Is
やerrors.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のエラー命名規則への準拠と、エラー変数の型定義の明確化を示しています。
-
InvalidPublicKeyError
からErrInvalidPublicKey
へのリネーム:- 元のコードでは
var InvalidPublicKeyError = invalidPublicKeyError(0)
となっていました。ここでinvalidPublicKeyError
はアンエクスポートされた(小文字で始まる)型です。InvalidPublicKeyError
という変数名は大文字で始まるためエクスポートされますが、その命名は「エラー型」のように見えてしまいます。 - 変更後、
var ErrInvalidPublicKey error = invalidPublicKeyError(0)
となりました。- 変数名が
ErrInvalidPublicKey
となり、Goの慣習に従いエラー値であることを明確に示しています。 error =
の部分が追加され、ErrInvalidPublicKey
が明示的にerror
インターフェース型であることを宣言しています。これにより、invalidPublicKeyError(0)
という具体的な型の値がerror
インターフェースに適合することを示し、コードの意図がより明確になります。これは、Goのインターフェースの暗黙的な実装(invalidPublicKeyError
がError() string
メソッドを持つためerror
インターフェースを満たす)を利用しつつ、エクスポートされる変数の型を明示的にerror
とすることで、外部からの利用者がその変数をerror
として扱うことを促します。
- 変数名が
- このリネームに伴い、
Sign
関数内でエラーを返す箇所もerr = InvalidPublicKeyError
からerr = ErrInvalidPublicKey
に変更されています。
- 元のコードでは
-
KeyIncorrectError
からErrKeyIncorrect
、UnknownIssuerError
からErrUnknownIssuer
へのリネーム:src/pkg/crypto/openpgp/errors/errors.go
でも同様に、KeyIncorrectError
とUnknownIssuerError
がそれぞれErrKeyIncorrect
とErrUnknownIssuer
にリネームされ、error
インターフェース型が明示的に宣言されています。- これらのエラー変数が使用されている他のファイル(
src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go
、src/pkg/crypto/openpgp/read.go
、src/pkg/crypto/openpgp/read_test.go
)でも、対応する参照が更新されています。
これらの変更は、Go言語のコードベース全体におけるエラーハンドリングの一貫性と可読性を高めるための、クリーンアップ作業の一環です。
関連リンク
- Effective Go - Errors: Go言語におけるエラーハンドリングの基本的な考え方と慣習について説明されています。
- Go Code Review Comments - Error Naming: Goのコードレビューにおけるエラー命名に関する推奨事項が記載されています。
参考にした情報源リンク
- Go言語公式ドキュメント
- Effective Go
- Go Code Review Comments (Go Wiki)
- GitHubのコミット履歴とdiff
- Go言語のエラーハンドリングに関する一般的な知識