Golang には通常、エラー センチネル (センチネル エラー)、エラー タイプ アサーション、エラー コール スタックの記録という 3 つのエラー処理メソッドがあります。エラーセンチネルとは、エラー処理分岐の判定条件として特定の値を持つ変数を使用することを指します。エラー タイプは、エラー処理ロジックをルーティングするために使用され、エラー監視と同じ効果があります。タイプ システムは、エラー タイプの一意性を提供します。エラー ブラック ボックスとは、エラーの種類をあまり気にせず、エラーを上位層に返すことを指します。アクションを実行する必要がある場合、エラーの種類ではなくエラーの動作についてアサーションを行う必要があります。
このチュートリアルの動作環境: Windows 7 システム、GO バージョン 1.18、Dell G3 コンピューター。
Golang は try-catch
のようなエラー処理機構を提供せず、設計レベルで C 言語スタイルのエラー処理を採用し、関数の戻り値を通じてエラー情報を返します。具体的な例は次のとおりです。 :
func ReturnError() (string, error) { return "", fmt.Errorf("Test Error") } func main() { val, err := ReturnError() if err != nil { panic(err) } fmt.Println(val) }
上記の例は、基本的なエラー処理の例です。運用環境で実行されるコール スタックは多くの場合非常に複雑で、返される error
もさまざまです。多くの場合、次のことが必要です。 return エラー メッセージによって、特定のエラー処理ロジックが決まります。
Golang には通常、エラー センチネル (Sentinel Error)、エラー タイプ アサーション (Error Type Asseration)、エラー コール スタックの記録という 3 つのエラー処理メソッドがあります。
Sentinel とは、エラー処理分岐の判定条件として特定の値を持つ変数を使用することを指します。一般的なアプリケーション シナリオには、# が含まれます。 gorm.##gorm.RecordNotFounded および Redis ライブラリ内の
redis.NIL。
error 型変数が同じアドレスを指している場合に限り、2 つの変数は等しくなりますが、それ以外の場合は等しくありません。
var ErrTest = errors.New("Test Error") err := doSomething() if err == ErrTest{ // TODO: Do With Error }Sentinel を使用する場合、次の問題があります。問題は 2 つあります: 1. コード構造が柔軟ではなく、分岐処理では
== しか使用できません。または
! = 判断してください このままではスパゲッティのようなコードが書きやすくなります。
var ErrTest1 = errors.New("ErrTest1") var ErrTest2 = errors.New("ErrTest1") var ErrTest3 = errors.New("ErrTest1") …… var ErrTestN = errors.New("ErrTestN") …… if err == ErrTest1{ …… } else if err == ErrTest2{ …… }else if err == ErrTest3{ …… } …… else err == ErrTestN{ …… }2. センチネル変数の値は変更できません。それ以外の場合はロジック エラーが発生します。上記の golang 記述メソッドのエラー センチネルは変更でき、次の方法で解決できます。 ##
type Error string func (e Error) Error() string { return string(e) }
3. センチネル変数はこれにより非常に強い結合が発生し、インターフェイスで新しいエラーが吐き出されることで、ユーザーはそれに応じてコードを変更し、新しい処理エラーを作成することになります。
上記のソリューションと比較すると、Error Sentinel には、変数ではなくインターフェイスに依存する、より洗練されたソリューションがあります。
var ErrTest1 = errors.New("ErrTest1") func IsErrTest1(err error) bool{ return err == ErrTest1 }エラー タイプ
type TestError { } func(err *TestError) Error() string{ return "Test Error" } if err, ok := err.(TestError); ok { //TODO 错误分支处理 } err := something() switch err := err.(type) { case nil: // call succeeded, nothing to do case *TestError: fmt.Println("error occurred on line:", err.Line) default: // unknown error }
センチネルと比較して、エラー タイプの不変性変更は良好であり、
switch を使用してエレガントなルーティング戦略を提供できます。しかし、これではユーザーがパッケージへの過度の依存を避けることができなくなります。 インターフェイスを使用してより複雑で多様なエラーをスローするには、呼び出し元のコードを変更する必要があります。
func fn() error{ x, err := Foo() if err != nil { return err } } func main(){ err := fn() if IsTemporary(err){ fmt.Println("Temporary Error") } } type temporary interface { Temporary() bool } // IsTemporary returns true if err is temporary. func IsTemporary(err error) bool { te, ok := err.(temporary) return ok && te.Temporary() }
このようにして、1. インターフェイス間の依存関係は直接分離され、2. エラー処理ルーティングはエラー タイプとは関係ありませんが、特定の動作に関連付けられ、エラー タイプの拡大を回避します。
まとめここで一文追加する必要があります。
ブラックボックス処理でエラーを返すというのは、エラーの存在を無視したり、直接無視したりするわけではありませんが、エラーを適切に処理する必要があります。適切な場所。このプロセスでは、errorsWrap、Zaplogging などを使用して、レイヤーごとにエラーが返されるときに呼び出し側リンクのコンテキスト情報を記録できます。 。
func authenticate() error{ return fmt.Errorf("authenticate") } func AuthenticateRequest() error { err := authenticate() // OR logger.Info("authenticate fail %v", err) if err != nil { return errors.Wrap(err, "AuthenticateRequest") } return nil } func main(){ err := AuthenticateRequest() fmt.Printf("%+v\n", err) fmt.Println("##########") fmt.Printf("%v\n", errors.Cause(err)) } // 打印信息 authenticate AuthenticateRequest main.AuthenticateRequest /Users/hekangle/MyPersonProject/go-pattern/main.go:17 main.main /Users/hekangle/MyPersonProject/go-pattern/main.go:23 runtime.main /usr/local/Cellar/go@1.13/1.13.12/libexec/src/runtime/proc.go:203 runtime.goexit /usr/local/Cellar/go@1.13/1.13.12/libexec/src/runtime/asm_amd64.s:1357 ########## authenticate【関連する推奨事項: Go ビデオ チュートリアル 、
プログラミング教育 ]
以上がgolangでエラーを処理する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。