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

[インデックス 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のエラーハンドリングの慣習として、以下の点が挙げられます。

  1. 明示的なエラーチェック: Goでは例外処理のメカニズム(try-catchなど)は提供されず、関数がエラーを返す場合は、その戻り値として error 型の値を返します。呼び出し元は、この error 値が nil でないかどうかを明示的にチェックし、エラーが発生した場合は適切に処理する必要があります。
  2. エラーメッセージのプレフィックス: 標準ライブラリや一般的なGoのコードでは、エラーメッセージにそのエラーを生成したパッケージ名やコンポーネント名をプレフィックスとして付与することが推奨されています。これにより、エラーメッセージを見ただけで、どの部分でエラーが発生したのかを容易に特定できます。例えば、io パッケージからのエラーは "io: " で始まり、os パッケージからのエラーは "os: " で始まります。
  3. エラーのラップ: Go 1.13以降では、fmt.Errorf%w 動詞や errors.Join を使用してエラーをラップ(wrap)する機能が導入されました。これにより、エラーチェーンを作成し、元のエラー情報を失うことなく、より詳細なコンテキストを追加できるようになりました。
  4. エラーの型: 特定のエラー条件を表すために、カスタムエラー型を定義することが一般的です。このコミットで変更されている StructuralErrorSyntaxError は、encoding/asn1 パッケージ内で定義されたカスタムエラー型です。これらの型は、error インターフェースを実装しており、Error() メソッドを通じてエラーメッセージを提供します。

このコミットは、特に「エラーメッセージのプレフィックス」に関するGoの慣習に準拠するための変更です。

技術的詳細

このコミットは、encoding/asn1 パッケージ内のエラーメッセージの生成方法を修正し、すべてのエラーメッセージが "asn1: " というプレフィックスで始まるように統一しています。

具体的な変更点は以下の通りです。

  1. StructuralErrorSyntaxErrorError() メソッドの変更:

    • StructuralError 型の Error() メソッドは、"ASN.1 structure error: " から "asn1: structure error: " に変更されました。
    • SyntaxError 型の Error() メソッドは、"ASN.1 syntax error: " から "asn1: syntax error: " に変更されました。 これにより、これらのカスタムエラー型が返す文字列が、Goのエラーメッセージの慣習に従うようになりました。
  2. SyntaxError のコンストラクタ呼び出しの変更:

    • parseBool 関数内で SyntaxError を生成する際に、引数として渡される文字列から "encoding/asn1: " というプレフィックスが削除されました。これは、SyntaxErrorError() メソッド自体が "asn1: " プレフィックスを追加するようになったため、二重にプレフィックスが付与されるのを避けるためです。
  3. 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.gosrc/pkg/encoding/asn1/marshal.go の2つのファイルに対する変更を示しています。

src/pkg/encoding/asn1/asn1.go の変更点

  1. 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 パッケージから発生したことが明確になります。

  2. 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: " に変更されました。これも同様に、エラーの発生源を明確にするための変更です。

  3. parseBool 関数内の SyntaxError 生成の変更:

    -		err = SyntaxError{"encoding/asn1: invalid boolean"}
    +		err = SyntaxError{"invalid boolean"}
    

    parseBool 関数内で SyntaxError を生成する際に、エラーメッセージの文字列から "encoding/asn1: " というプレフィックスが削除されました。これは、SyntaxError.Error() メソッド自体が "asn1: " プレフィックスを追加するようになったため、二重にプレフィックスが付与されるのを避けるためです。

  4. 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 の変更点

  1. marshalUTCTime 関数内の StructuralError 生成の変更:

    -		return StructuralError{"Cannot represent time as UTCTime"}
    +		return StructuralError{"cannot represent time as UTCTime"}
    

    StructuralError を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。

  2. marshalField 関数内の StructuralError 生成の変更 (1):

    -		return StructuralError{"Explicit string type given to non-string member"}
    +		return StructuralError{"explicit string type given to non-string member"}
    

    StructuralError を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。

  3. marshalField 関数内の StructuralError 生成の変更 (2):

    -			return StructuralError{"Non sequence tagged as set"}
    +			return StructuralError{"non sequence tagged as set"}
    

    StructuralError を生成する際に、エラーメッセージの先頭の文字が小文字に変更されました。

これらの変更は、Goのエラーメッセージの慣習に従い、すべての encoding/asn1 パッケージのエラーが "asn1: " で始まるように統一することで、エラーの可読性と解析のしやすさを向上させています。

関連リンク

参考にした情報源リンク