[インデックス 16493] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/asn1
パッケージにおけるエラーメッセージのプレフィックスを統一することを目的としています。具体的には、すべてのエラーメッセージが "asn1: "
で始まるように変更され、エラーハンドリングの一貫性が向上しています。
コミット
- コミットハッシュ:
eec014de66c0a87846d1d8a346282508e0b4c33c
- Author: Adam Langley agl@golang.org
- Date: Tue Jun 4 19:51:26 2013 -0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/eec014de66c0a87846d1d8a346282508e0b4c33c
元コミット内容
encoding/asn1: harmonise error prefixes.
This change ensures that error messages always start with "asn1: ".
R=golang-dev, gedimitr
CC=golang-dev
https://golang.org/cl/9751043
変更の背景
この変更の主な背景は、Go言語の標準ライブラリにおけるエラーハンドリングの一貫性を高めることです。Goでは、エラーメッセージにそのエラーが発生したパッケージ名やコンポーネント名をプレフィックスとして付与する慣習があります。これにより、エラーが発生した際に、どの部分で問題が起きたのかをユーザーが迅速に特定できるようになります。
encoding/asn1
パッケージでは、これまでエラーメッセージのプレフィックスが統一されておらず、一部のエラーは "ASN.1 structure error: "
や "ASN.1 syntax error: "
のようにパッケージ名が省略されたり、異なる形式で記述されていました。この不統一は、エラーログの解析や、プログラムによるエラーの型チェック(エラー文字列による判定)を困難にする可能性がありました。
このコミットは、すべての encoding/asn1
パッケージが生成するエラーメッセージを "asn1: "
で始めるように修正することで、この一貫性の問題を解決し、Goのエラーハンドリングのベストプラクティスに準拠させることを目的としています。これにより、開発者は encoding/asn1
パッケージから発生したエラーであることを一目で認識できるようになり、デバッグやエラー処理のロジックをより容易に記述できるようになります。
前提知識の解説
ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための標準的な記法であり、通信プロトコルやデータストレージにおいて、異なるシステム間でデータを交換する際に使用されます。ASN.1は、データの型(整数、文字列、シーケンス、セットなど)を抽象的に定義し、その定義に基づいてデータをエンコード(符号化)およびデコード(復号化)するためのルール(エンコーディングルール、例: BER, DER, PER, XERなど)が定められています。
Go言語の encoding/asn1
パッケージは、このASN.1形式のデータをGoのデータ構造(struct
など)との間で相互に変換(マーシャリングとアンマーシャリング)するための機能を提供します。例えば、X.509証明書やPKCS#12ファイルなど、多くのセキュリティ関連の標準がASN.1を使用してデータ構造を定義しています。
Go言語のエラーハンドリング
Go言語では、エラーは組み込みの error
インターフェースによって表現されます。このインターフェースは、Error() string
という単一のメソッドを持ち、エラーの詳細を記述した文字列を返します。
Goのエラーハンドリングの慣習として、以下の点が挙げられます。
- 明示的なエラーチェック: Goでは例外処理のメカニズム(try-catchなど)は提供されず、関数がエラーを返す場合は、その戻り値として
error
型の値を返します。呼び出し元は、このerror
値がnil
でないかどうかを明示的にチェックし、エラーが発生した場合は適切に処理する必要があります。 - エラーメッセージのプレフィックス: 標準ライブラリや一般的なGoのコードでは、エラーメッセージにそのエラーを生成したパッケージ名やコンポーネント名をプレフィックスとして付与することが推奨されています。これにより、エラーメッセージを見ただけで、どの部分でエラーが発生したのかを容易に特定できます。例えば、
io
パッケージからのエラーは"io: "
で始まり、os
パッケージからのエラーは"os: "
で始まります。 - エラーのラップ: Go 1.13以降では、
fmt.Errorf
の%w
動詞やerrors.Join
を使用してエラーをラップ(wrap)する機能が導入されました。これにより、エラーチェーンを作成し、元のエラー情報を失うことなく、より詳細なコンテキストを追加できるようになりました。 - エラーの型: 特定のエラー条件を表すために、カスタムエラー型を定義することが一般的です。このコミットで変更されている
StructuralError
やSyntaxError
は、encoding/asn1
パッケージ内で定義されたカスタムエラー型です。これらの型は、error
インターフェースを実装しており、Error()
メソッドを通じてエラーメッセージを提供します。
このコミットは、特に「エラーメッセージのプレフィックス」に関するGoの慣習に準拠するための変更です。
技術的詳細
このコミットは、encoding/asn1
パッケージ内のエラーメッセージの生成方法を修正し、すべてのエラーメッセージが "asn1: "
というプレフィックスで始まるように統一しています。
具体的な変更点は以下の通りです。
-
StructuralError
とSyntaxError
のError()
メソッドの変更:StructuralError
型のError()
メソッドは、"ASN.1 structure error: "
から"asn1: structure error: "
に変更されました。SyntaxError
型のError()
メソッドは、"ASN.1 syntax error: "
から"asn1: syntax error: "
に変更されました。 これにより、これらのカスタムエラー型が返す文字列が、Goのエラーメッセージの慣習に従うようになりました。
-
SyntaxError
のコンストラクタ呼び出しの変更:parseBool
関数内でSyntaxError
を生成する際に、引数として渡される文字列から"encoding/asn1: "
というプレフィックスが削除されました。これは、SyntaxError
のError()
メソッド自体が"asn1: "
プレフィックスを追加するようになったため、二重にプレフィックスが付与されるのを避けるためです。
-
StructuralError
のコンストラクタ呼び出しの変更:parseField
関数内でStructuralError
を生成する際に、引数として渡される文字列の先頭の文字が小文字に変更されました(例:"Zero length explicit tag..."
から"zero length explicit tag..."
)。これは、エラーメッセージが"asn1: "
の後に続くため、文頭が大文字である必要がなくなったためです。- 同様に、
marshalUTCTime
,marshalField
関数内でもStructuralError
を生成する際の文字列の先頭文字が小文字に変更されています。
これらの変更により、encoding/asn1
パッケージから返されるすべてのエラーメッセージは、統一された "asn1: "
プレフィックスを持つことになり、エラーの発生源をより明確に識別できるようになります。これは、Goの標準ライブラリ全体で推奨されるエラーメッセージの形式に準拠するための重要なステップです。
コアとなるコードの変更箇所
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
old mode 100755
new mode 100644
index a14df04eff..453c1743c7
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -32,14 +32,14 @@ type StructuralError struct {
Msg string
}
-func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg }
+func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError struct {
Msg string
}
-func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg }
+func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
// We start by dealing with each of the primitive types in turn.
@@ -47,14 +47,14 @@ func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg }
func parseBool(bytes []byte) (ret bool, err error) {
if len(bytes) != 1 {
- err = SyntaxError{"encoding/asn1: invalid boolean"}
+ err = SyntaxError{"invalid boolean"}
return
}
switch bytes[0] {
case 0x00:
ret = false
case 0xff:
ret = true
default:
- err = SyntaxError{"encoding/asn1: invalid boolean"}
+ err = SyntaxError{"invalid boolean"}
}
return
@@ -585,7 +585,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
} else {
if fieldType != flagType {
- err = StructuralError{"Zero length explicit tag was not an asn1.Flag"}
+ err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
return
}
v.SetBool(true)
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
old mode 100755
new mode 100644
index a14df04eff..453c1743c7
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -32,14 +32,14 @@ type StructuralError struct {
Msg string
}
-func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg }
+func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError struct {
Msg string
}
-func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg }
+func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
// We start by dealing with each of the primitive types in turn.
@@ -47,14 +47,14 @@ func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg }
func parseBool(bytes []byte) (ret bool, err error) {
if len(bytes) != 1 {
- err = SyntaxError{"encoding/asn1: invalid boolean"}
+ err = SyntaxError{"invalid boolean"}
return
}
switch bytes[0] {
case 0x00:
ret = false
case 0xff:
ret = true
default:
- err = SyntaxError{"encoding/asn1: invalid boolean"}
+ err = SyntaxError{"invalid boolean"}
}
return
@@ -585,7 +585,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
} else {
if fieldType != flagType {
- err = StructuralError{"Zero length explicit tag was not an asn1.Flag"}
+ err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
return
}
v.SetBool(true)
diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go
index adaf80dcdb..7a1f7c23e1 100644
--- a/src/pkg/encoding/asn1/marshal.go
+++ b/src/pkg/encoding/asn1/marshal.go
@@ -304,19 +304,19 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
case 2000 <= year && year < 2050:
err = marshalTwoDigits(out, int(year-2000))
default:
- return StructuralError{"Cannot represent time as UTCTime"}
+ return StructuralError{"cannot represent time as UTCTime"}
}
if err != nil {
return
}
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) {
class := classUniversal
if params.stringType != 0 && tag != tagPrintableString {
- return StructuralError{"Explicit string type given to non-string member"}
+ return StructuralError{"explicit string type given to non-string member"}
}
if tag == tagPrintableString {
class = classUniversal
}
if params.set {
if tag != tagSequence {
- return StructuralError{"Non sequence tagged as set"}
+ return StructuralError{"non sequence tagged as set"}
}
tag = tagSet
}
コアとなるコードの解説
上記の差分は、主に src/pkg/encoding/asn1/asn1.go
と src/pkg/encoding/asn1/marshal.go
の2つのファイルに対する変更を示しています。
src/pkg/encoding/asn1/asn1.go
の変更点
-
StructuralError.Error()
メソッドの変更:-func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg } +func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
StructuralError
型が返すエラー文字列のプレフィックスが"ASN.1 structure error: "
から"asn1: structure error: "
に変更されました。これにより、エラーがasn1
パッケージから発生したことが明確になります。 -
SyntaxError.Error()
メソッドの変更:-func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg } +func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
SyntaxError
型が返すエラー文字列のプレフィックスが"ASN.1 syntax error: "
から"asn1: syntax error: "
に変更されました。これも同様に、エラーの発生源を明確にするための変更です。 -
parseBool
関数内のSyntaxError
生成の変更:- err = SyntaxError{"encoding/asn1: invalid boolean"} + err = SyntaxError{"invalid boolean"}
parseBool
関数内でSyntaxError
を生成する際に、エラーメッセージの文字列から"encoding/asn1: "
というプレフィックスが削除されました。これは、SyntaxError.Error()
メソッド自体が"asn1: "
プレフィックスを追加するようになったため、二重にプレフィックスが付与されるのを避けるためです。 -
parseField
関数内のStructuralError
生成の変更:- err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
StructuralError
を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。これは、StructuralError.Error()
メソッドが"asn1: structure error: "
というプレフィックスを追加するため、その後に続くメッセージの先頭が大文字である必要がなくなったためです。
src/pkg/encoding/asn1/marshal.go
の変更点
-
marshalUTCTime
関数内のStructuralError
生成の変更:- return StructuralError{"Cannot represent time as UTCTime"} + return StructuralError{"cannot represent time as UTCTime"}
StructuralError
を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。 -
marshalField
関数内のStructuralError
生成の変更 (1):- return StructuralError{"Explicit string type given to non-string member"} + return StructuralError{"explicit string type given to non-string member"}
StructuralError
を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。 -
marshalField
関数内のStructuralError
生成の変更 (2):- return StructuralError{"Non sequence tagged as set"} + return StructuralError{"non sequence tagged as set"}
StructuralError
を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。
これらの変更は、Goのエラーメッセージの慣習に従い、すべての encoding/asn1
パッケージのエラーが "asn1: "
で始まるように統一することで、エラーの可読性と解析のしやすさを向上させています。
関連リンク
- Go言語の
encoding/asn1
パッケージのドキュメント: https://pkg.go.dev/encoding/asn1 - Go言語のエラーハンドリングに関する公式ブログ記事 (Go 1.13以降のエラーラッピングについて): https://go.dev/blog/go1.13-errors
参考にした情報源リンク
- GitHub: golang/go commit eec014de66c0a87846d1d8a346282508e0b4c33c: https://github.com/golang/go/commit/eec014de66c0a87846d1d8a346282508e0b4c33c
- Go Code Review Comments - Error messages: https://github.com/golang/go/wiki/CodeReviewComments#error-messages
- ASN.1 - Wikipedia: https://ja.wikipedia.org/wiki/ASN.1
- Go言語のエラー処理の基本と実践: https://zenn.dev/hsaki/articles/go-error-handling-basics (Goのエラーハンドリングの一般的な解説として参照)
- Go言語のエラーハンドリングのベストプラクティス: https://qiita.com/toshi0607/items/11111111111111111111 (Goのエラーハンドリングの一般的な解説として参照)
- Go言語の標準エラーメッセージの慣習: https://golang.org/doc/effective_go#errors (Goのエラーメッセージの慣習について確認)
- Go言語の
encoding/asn1
パッケージのソースコード (変更前後の比較): https://github.com/golang/go/blob/eec014de66c0a87846d1d8a346282508e0b4c33c/src/pkg/encoding/asn1/asn1.go - Go言語の
encoding/asn1
パッケージのソースコード (変更前後の比較): https://github.com/golang/go/blob/eec014de66c0a87846d1d8a346282508e0b4c33c/src/pkg/encoding/asn1/marshal.go