Goのエラーに追加情報を追加する方法
序章
Goの関数が失敗すると、関数はを使用して値を返します。 error
呼び出し元がその障害を処理できるようにするためのインターフェース。 多くの場合、開発者はfmtパッケージのfmt.Errorf関数を使用してこれらの値を返します。 ただし、Go 1.13より前では、この関数を使用することの欠点は、エラーが返される原因となった可能性のあるエラーに関する情報が失われることです。 これを解決するために、開発者はパッケージを使用してエラーを他のエラー内に「ラップ」する方法を提供するか、またはを実装してカスタムエラーを作成します。 Error() string
それらの1つの方法 struct
エラーの種類。 これらを作成するのは面倒な場合があります struct
ただし、呼び出し元が明示的に処理する必要のないエラーが多数ある場合はタイプします。そのため、Go 1.13では、言語によってこれらのケースの処理を容易にする機能が追加されました。
1つの機能は、を使用してエラーをラップする機能です。 fmt.Errorf
で機能する error
ラップされたエラーにアクセスするために後でラップを解除できる値。 これにより、エラーラッピング機能がGo標準ライブラリに組み込まれるため、サードパーティのライブラリを使用する必要がなくなります。
さらに、関数errors.Isおよびerrors.Asを使用すると、特定のエラーが特定のエラー内のどこかにラップされているかどうかを簡単に判断でき、その特定のエラーにアクセスできます。すべてのエラーを自分でアンラップする必要なしに直接。
このチュートリアルでは、これらの関数を使用して、関数から返されるエラーに追加情報を含めるプログラムを作成してから、独自のカスタムエラーを作成します。 struct
ラッピングおよびアンラッピング機能をサポートします。
前提条件
このチュートリアルに従うには、次のものが必要です。
- バージョン1.13以降をインストールしてください。 これを設定するには、オペレーティングシステムのGoチュートリアルをインストールする方法に従ってください。
- (オプション)Go でのエラー処理を読むことは、エラー処理のより詳細な説明のためにこのチュートリアルで役立つかもしれませんが、このチュートリアルは、より高いレベルで同じトピックのいくつかもカバーします。
- (オプション)このチュートリアルは、 Go チュートリアルでのカスタムエラーの作成を拡張し、元のチュートリアル以降にGoに追加された機能を備えています。 前のチュートリアルを読むことは役に立ちますが、厳密には必須ではありません。
Goでのエラーの返却と処理
プログラムでエラーが発生した場合は、ユーザーに表示されないようにそれらのエラーを処理することをお勧めしますが、エラーを処理するには、最初にそれらについて知る必要があります。 Goでは、特別な機能を使用して関数からエラーに関する情報を返すことにより、プログラムのエラーを処理できます。 interface
タイプ、 error
インターフェース。 を使用して error
インターフェイスを使用すると、任意のGoタイプを error
そのタイプが Error() string
メソッドが定義されました。 Go標準ライブラリは、作成する機能を提供します error
■fmt.Errorf関数など、これらの戻り値の場合。
このセクションでは、を使用する関数を使用してプログラムを作成します fmt.Errorf
エラーを返すには、関数が返す可能性のあるエラーをチェックするためのエラーハンドラーも追加します。 (Goでのエラー処理の詳細については、チュートリアル Go でのエラー処理を参照してください。)
多くの開発者は、現在のプロジェクトを保持するためのディレクトリを持っています。 このチュートリアルでは、という名前のディレクトリを使用します projects
.
まず、 projects
ディレクトリとそれに移動します:
- mkdir projects
- cd projects
から projects
ディレクトリ、新しいを作成します errtutorial
新しいプログラムを保持するディレクトリ:
- mkdir errtutorial
次に、次のコマンドを使用して新しいディレクトリに移動します。 cd
指図:
- cd errtutorial
あなたが入ったら errtutorial
ディレクトリ、を使用します go mod init
名前の付いた新しいモジュールを作成するコマンド errtutorial
:
- go mod init errtutorial
Goモジュールを作成したら、という名前のファイルを開きます main.go
の中に errtutorial
ディレクトリを使用して nano
、またはお気に入りのエディター:
- nano main.go
次に、プログラムを作成します。 プログラムは数字をループします 1
終えた 3
と呼ばれる関数を使用して、それらの数値が有効かどうかを判断してみてください validateValue
. 番号が無効であると判断された場合、プログラムは fmt.Errorf
を生成する関数 error
関数から返される値。 The fmt.Errorf
関数を使用すると、 error
エラーメッセージが関数に提供するメッセージである値。 と同様に機能します fmt.Printf
、ただし、メッセージを画面に出力する代わりに、メッセージを次のように返します。 error
代わりは。
次に、 main
関数の場合、エラー値がチェックされ、それが nil
値かどうか。 それが nil
値、関数が成功し、 valid!
メッセージが印刷されます。 そうでない場合は、受信したエラーが代わりに出力されます。
プログラムを開始するには、次のコードをに追加します main.go
ファイル:
package main
import (
"fmt"
)
func validateValue(number int) error {
if number == 1 {
return fmt.Errorf("that's odd")
} else if number == 2 {
return fmt.Errorf("uh oh")
}
return nil
}
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := validateValue(num)
if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
The validateValue
プログラムの関数は数値を受け取り、 error
有効な値であると判断されたかどうかに基づきます。 このプログラムでは、番号 1
は無効であり、エラーを返します that's odd
. 数字 2
は無効であり、エラーを返します uh oh
. The validateValue
関数はを使用します fmt.Errorf
を生成する関数 error
返される値。 The fmt.Errorf
関数は、次のようなフォーマットを使用してエラーメッセージをフォーマットできるため、エラーを返すのに便利です。 fmt.Printf
また fmt.Sprintf
その後、それを渡す必要はありません string
に errors.New
.
の中に main
関数、 for
ループは、からの各番号を繰り返すことから始まります 1
に 3
値をに保存します num
変数。 ループ本体の内側で、 fmt.Printf
プログラムが現在検証している番号を出力します。 次に、 validateValue
関数とパスイン num
、検証中の現在の番号、およびエラー結果をに保存します err
変数。 最後に、 err
ではありません nil
これは、検証中にエラーが発生したことを意味し、エラーメッセージは fmt.Println
. The else
エラーチェックの句が出力されます "valid!"
エラーが発生しなかったとき。
変更を保存した後、を使用してプログラムを実行します go run
でコマンド main.go
からの議論として errtutorial
ディレクトリ:
- go run main.go
プログラムの実行からの出力は、検証が各番号と番号に対して実行されたことを示します 1
と番号 2
適切なエラーを返しました:
Outputvalidating 1... there was an error: that's odd
validating 2... there was an error: uh oh
validating 3... valid!
プログラムからの出力を見ると、プログラムが3つの数値すべてを検証しようとしていることがわかります。 初めてそれが言う validateValue
関数が返した that's odd
エラー。これは、 1
. 次の値、 2
、エラーが返されたことも示していますが、 uh oh
今回はエラー。 最後に、 3
値が返されます nil
エラー値は、エラーがなく、数値が有効であることを意味します。 方法 validateValue
関数が書かれている、 nil
エラー値は、どちらでもない値に対して返されます 1
また 2
.
このセクションでは、 fmt.Errorf
作成する error
関数から返した値。 また、エラーハンドラーを追加して、エラーメッセージが表示されたときに出力します。 error
関数から返されます。 ただし、エラーが発生したことだけでなく、エラーの意味を知ることが役立つ場合もあります。 次のセクションでは、特定のケースに合わせてエラー処理をカスタマイズする方法を学習します。
Sentinelエラーを使用した特定のエラーの処理
あなたが受け取るとき error
関数からの値、最も基本的なエラー処理は、 error
値は nil
か否か。 これにより、関数にエラーがあったかどうかがわかりますが、特定のエラーケースに合わせてエラー処理をカスタマイズしたい場合があります。 たとえば、リモートサーバーに接続しているコードがあり、返されるエラー情報は「エラーが発生した」だけであるとします。 エラーの原因がサーバーを利用できなかったのか、接続資格情報が無効だったのかを確認することをお勧めします。 エラーがユーザーの資格情報が間違っていることを意味していることがわかっている場合は、すぐにユーザーに通知することをお勧めします。 ただし、エラーがサーバーを利用できないことを意味する場合は、ユーザーに通知する前に、再接続を数回試行することをお勧めします。 これらのエラーの違いを判断することで、より堅牢でユーザーフレンドリーなプログラムを作成できます。
特定のタイプのエラーをチェックする1つの方法は、 Error
のメソッド error
タイプしてエラーからメッセージを取得し、その値を探しているエラーのタイプと比較します。 プログラムで、以外のメッセージを表示したいとします。 there was an error: uh oh
エラー値が uh oh
. このケースを処理するための1つのアプローチは、から返された値を確認することです。 Error
そのような方法:
if err.Error() == "uh oh" {
// Handle 'uh oh' error.
fmt.Println("oh no!")
}
の文字列値を確認する err.Error()
それが価値があるかどうかを確認する uh oh
、上記のコードのように、この場合は機能します。 しかし、次の場合、コードは機能しません uh oh
エラー string
プログラムの他の場所では少し異なります。 この方法でエラーをチェックすると、エラーがチェックされるすべての場所を更新する必要があるため、エラーのメッセージ自体を更新する必要がある場合にも、コードが大幅に更新される可能性があります。 たとえば、次のコードを考えてみましょう。
func giveMeError() error {
return fmt.Errorf("uh h")
}
err := giveMeError()
if err.Error() == "uh h" {
// "uh h" error code
}
このコードでは、エラーメッセージにタイプミスが含まれており、 o
の uh oh
. これに気づき、ある時点で修正されたが、いくつかの場所でこのエラーチェックを追加した後でのみ、それらすべての場所でチェックを更新する必要があります。 err.Error() == "uh oh"
. 1つが欠落している場合、これは1文字の変更のみであるため簡単ですが、予期されているカスタムエラーハンドラーは、予期されているため実行されません。 uh h
ではなく uh oh
.
このような場合、特定のエラーを他のエラーとは異なる方法で処理したい場合は、エラー値を保持することを目的とした変数を作成するのが一般的です。 このようにして、コードは文字列ではなくその変数をチェックできます。 通常、これらの変数は次のいずれかで始まります err
また Err
それらの名前に、それらがエラーであることを示します。 エラーが定義されているパッケージ内でのみ使用されることを意図している場合は、 err
プレフィックス。 エラーが他の場所で使用されることを意図している場合は、代わりに Err
関数や struct
.
ここで、前のタイプミスの例でこれらのエラー値の1つを使用していたとしましょう。
var errUhOh = fmt.Errorf("uh h")
func giveMeError() error {
return errUhOh
}
err := giveMeError()
if err == errUhOh {
// "uh oh" error code
}
この例では、変数 errUhOh
「uhoh」エラーのエラー値として定義されます(スペルが間違っている場合でも)。 The giveMeError
関数はの値を返します errUhOh
「ええと」エラーが発生したことを発信者に知らせたいからです。 次に、エラー処理コードは err
から返される値 giveMeError
に対して errUhOh
「ええと」エラーが発生したかどうかを確認します。 タイプミスが見つかって修正された場合でも、エラーチェックは次の値をチェックしているため、すべてのコードは引き続き機能します。 errUhOh
、およびの値 errUhOh
は、エラー値の修正バージョンです。 giveMeError
戻ってきました。
この方法でチェックおよび比較することを目的としたエラー値は、センチネルエラーと呼ばれます。 センチネルエラーは、特定の意味で常に比較できる一意の値になるように設計されたエラーです。 The errUhOh
上記の値は常に同じ意味を持ち、「uh oh」エラーが発生したため、プログラムはエラーを次のように比較することに依存できます。 errUhOh
そのエラーが発生したかどうかを判断します。
Go標準ライブラリは、Goプログラムの開発時に利用できるいくつかのセンチネルエラーも定義しています。 1つの例は、sql.ErrNoRowsエラーです。 The sql.ErrNoRows
データベースクエリが結果を返さない場合はエラーが返されるため、接続エラーとは異なる方法でエラーを処理できます。 これはセンチネルエラーであるため、エラーチェックコードと比較して、クエリが行を返さない場合を知ることができ、プログラムは他のエラーとは異なる方法でそれを処理できます。
一般に、センチネルエラー値を作成する場合、errorsパッケージのerrors.New関数が代わりに使用されます。 fmt.Errorf
これまで使用してきた関数。 使用する errors.New
それ以外の fmt.Errorf
ただし、エラーの動作に基本的な変更は加えられず、ほとんどの場合、両方の関数を互換的に使用できます。 2つの最大の違いは errors.New
関数は静的メッセージと fmt.Errorf
関数を使用すると、次のような値で文字列をフォーマットできます。 fmt.Printf
また fmt.Sprintf
. センチネルエラーは値が変化しない基本的なエラーであるため、使用するのが一般的です errors.New
それらを作成します。
次に、プログラムを更新して、「uhoh」エラーの代わりにセンチネルエラーを使用するようにします。 fmt.Errorf
.
まず、 main.go
新しいファイルを追加します errUhOh
歩哨エラーとそれを使用するようにプログラムを更新します。 The validateValue
関数が更新され、使用する代わりにセンチネルエラーが返されるようになりました fmt.Errorf
. The main
関数が更新され、 errUhOh
歩哨エラーと印刷 oh no!
代わりにそれに遭遇したとき there was an error:
他のエラーに対して表示されるメッセージ。
package main
import (
"errors"
"fmt"
)
var (
errUhOh = errors.New("uh oh")
)
func validateValue(number int) error {
if number == 1 {
return fmt.Errorf("that's odd")
} else if number == 2 {
return errUhOh
}
return nil
}
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := validateValue(num)
if err == errUhOh {
fmt.Println("oh no!")
} else if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
次に、コードを保存して使用します go run
プログラムを再度実行するには:
- go run main.go
今回の出力には、の一般的なエラー出力が表示されます。 1
値ですが、カスタムを使用します oh no!
それが見たときのメッセージ errUhOh
から返されたエラー validateValue
為に 2
:
Outputvalidating 1... there was an error: that's odd
validating 2... oh no!
validating 3... valid!
エラーチェック内でセンチネルエラーを使用すると、特別なエラーケースの処理が容易になります。 たとえば、 io.EOF センチネルエラーによって示されるファイルの終わりに到達したために、読み取っているファイルが失敗しているかどうか、または一部のファイルが失敗しているかどうかを判断するのに役立ちます。他の理由。
このセクションでは、以下を使用してセンチネルエラーを使用するGoプログラムを作成しました errors.New
特定のタイプのエラーがいつ発生したかを示します。 ただし、プログラムが大きくなるにつれて、エラーに含まれる情報だけでなく、より多くの情報が必要になる場合があります。 uh oh
エラー値。 このエラー値は、エラーが発生した場所や発生した理由に関するコンテキストを提供するものではなく、大規模なプログラムでエラーの詳細を追跡するのは難しい場合があります。 トラブルシューティングを支援し、デバッグの時間を短縮するために、エラーラッピングを利用して必要な詳細を含めることができます。
ラッピングおよびアンラッピングエラー
エラーをラップするということは、ラップされたギフトのように、あるエラー値を取得し、その中に別のエラー値を入れることを意味します。 ただし、包装されたギフトと同様に、中身を知るには包装を解く必要があります。 エラーをラップすると、元のエラー値を失うことなく、エラーの発生場所や発生方法に関する追加情報を含めることができます。これは、エラーがラッパー内にあるためです。
Go 1.13より前では、元のエラーを含むカスタムエラー値を作成できたため、エラーをラップすることができました。 ただし、独自のラッパーを作成するか、すでに作業を行っているライブラリを使用する必要があります。 ただし、Go 1.13では、 errors.Unwrap関数と %w
動詞 fmt.Errorf
関数。 このセクションでは、プログラムを更新して、 %w
エラーをより多くの情報でラップする動詞、そしてあなたはそれから使用します errors.Unwrap
ラップされた情報を取得します。
エラーのラッピング fmt.Errorf
エラーをラップおよびアンラップするときに調べる最初の機能は、既存の機能への追加です fmt.Errorf
関数。 過去に、 fmt.Errorf
次のような動詞を使用して追加情報を含むフォーマットされたエラーメッセージを作成するために使用されました %s
文字列と %v
一般的な値の場合。 Go 1.13は、特別な場合の新しい動詞を追加しました。 %w
動詞。 いつ %w
動詞はフォーマット文字列に含まれ、 error
値に提供され、から返されるエラー fmt.Errorf
の値が含まれます error
作成中のエラーにラップされています。
今、開きます main.go
ファイルを更新して、という新しい関数を含めるようにします runValidation
. この関数は、現在検証されている番号を取得し、その番号に対して必要な検証を実行します。 この場合、実行する必要があるのは validateValue
関数。 値の検証でエラーが発生した場合は、次を使用してエラーをラップします。 fmt.Errorf
そしてその %w
あったことを示す動詞 run error
それが発生した場合は、その新しいエラーを返します。 また、更新する必要があります main
呼び出す代わりに関数 validateValue
直接呼び出す runValidation
代わりは:
...
var (
errUhOh = errors.New("uh oh")
)
func runValidation(number int) error {
err := validateValue(number)
if err != nil {
return fmt.Errorf("run error: %w", err)
}
return nil
}
...
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := runValidation(num)
if err == errUhOh {
fmt.Println("oh no!")
} else if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
更新を保存したら、を使用して更新されたプログラムを実行します go run
:
- go run main.go
出力は次のようになります。
Outputvalidating 1... there was an error: run error: that's odd
validating 2... there was an error: run error: uh oh
validating 3... valid!
この出力で確認することがいくつかあります。 まず、値のエラーメッセージが出力されます 1
今含まれています run error: that's odd
エラーメッセージで。 これは、エラーがによってラップされたことを示しています runValidation
の fmt.Errorf
ラップされているエラーの値は、 that's odd
、はエラーメッセージに含まれています。
次に、しかし、問題があります。 のために追加された特別なエラー処理 errUhOh
エラーは実行されていません。 を検証する行を見ると 2
入力すると、デフォルトのエラーメッセージが表示されます。 there was an error: run error: uh oh
期待される代わりに oh no!
メッセージ。 あなたは知っています validateValue
関数はまだ uh oh
ラップされたエラーの最後に表示されるため、エラーが発生しますが、 errUhOh
動作しなくなりました。 これは、によって返されるエラーが原因で発生します runValidation
もうありません errUhOh
、それはによって作成されたラップされたエラーです fmt.Errorf
. いつ if
ステートメントは比較しようとします err
に可変 errUhOh
、falseを返します。 err
と等しくない errUhOh
これ以上、wrappingのエラーと同じです。 errUhOh
. 修正するには errUhOh
エラーチェックでは、ラッパー内からエラーを取得する必要があります。 errors.Unwrap
関数。
でエラーをアンラップ errors.Unwrap
に加えて %w
Go 1.13で動詞が追加され、Go errorsパッケージにいくつかの新しい関数が追加されました。 これらの1つ、 errors.Unwrap
関数、取る error
パラメータとして、渡されたエラーがエラーラッパーの場合、ラップされたエラーが返されます error
. の場合 error
ラッパーでない場合、関数は nil
.
今、開きます main.go
再度ファイルし、を使用して errors.Unwrap
、更新します errUhOh
エラーチェックは、次の場合を処理します errUhOh
エラーラッパー内にラップされています:
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := runValidation(num)
if err == errUhOh || errors.Unwrap(err) == errUhOh {
fmt.Println("oh no!")
} else if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
編集内容を保存した後、プログラムを再度実行します。
- go run main.go
出力は次のようになります。
Outputvalidating 1... there was an error: run error: that's odd
validating 2... oh no!
validating 3... valid!
これで、出力に次のように表示されます。 oh no!
のエラー処理 2
入力値が戻ってきました。 追加 errors.Unwrap
追加した関数呼び出し if
ステートメントはそれが検出することを可能にします errUhOh
両方の場合 err
それ自体は errUhOh
値と同様に err
直接ラッピングしているエラーです errUhOh
.
このセクションでは、 %w
動詞が追加されました fmt.Errorf
ラップする errUhOh
別のエラー内のエラーとそれに追加情報を与えます。 次に、 errors.Unwrap
アクセスするには errorUhOh
別のエラーにラップされているエラー。 他のエラーの中にエラーを含める string
値はエラーメッセージを読む人間にとっては問題ありませんが、HTTPリクエストエラーのステータスコードなど、プログラムがエラーを処理するのを支援するために、エラーラッパーに追加情報を含めたい場合があります。 これが発生した場合、返す新しいカスタムエラーを作成できます。
カスタムラップエラー
Goの唯一のルールは error
インターフェースはそれが含まれているということです Error
メソッドでは、多くのGoタイプをカスタムエラーに変えることができます。 1つの方法は、 struct
エラーに関する追加情報を入力し、さらに Error
方法。
検証エラーの場合、どの値が実際にエラーを引き起こしたかを知ることが役立つ場合があります。 次に、新しいものを作成しましょう ValueError
のフィールドを含む構造体 Value
エラーと Err
実際の検証エラーを含むフィールド。 カスタムエラータイプは通常、 Error
タイプ名の末尾にある接尾辞は、タイプ名がに準拠するタイプであることを示します。 error
インターフェース。
あなたの main.go
ファイルを作成し、新しい ValueError
エラー構造体、および newValueError
エラーのインスタンスを作成する関数。 また、というメソッドを作成する必要があります Error
為に ValueError
したがって、構造体は error
. これ Error
メソッドは、エラーが文字列に変換されるたびに表示する値を返す必要があります。 この場合、それは使用します fmt.Sprintf
を示す文字列を返す value error:
次に、ラップされたエラー。 また、更新します validateValue
関数なので、基本的なエラーだけを返すのではなく、 newValueError
カスタムエラーを返す関数:
...
var (
errUhOh = fmt.Errorf("uh oh")
)
type ValueError struct {
Value int
Err error
}
func newValueError(value int, err error) *ValueError {
return &ValueError{
Value: value,
Err: err,
}
}
func (ve *ValueError) Error() string {
return fmt.Sprintf("value error: %s", ve.Err)
}
...
func validateValue(number int) error {
if number == 1 {
return newValueError(number, fmt.Errorf("that's odd"))
} else if number == 2 {
return newValueError(number, errUhOh)
}
return nil
}
...
更新が保存されたら、でプログラムを再実行します go run
:
- go run main.go
出力は次のようになります。
Outputvalidating 1... there was an error: run error: value error: that's odd
validating 2... there was an error: run error: value error: uh oh
validating 3... valid!
出力には、エラーが内部にラップされていることが表示されます。 ValueError
によって value error:
出力でそれらの前に。 しかし uh oh
エラー検出は、次の理由で再び壊れます。 errUhOh
ラッパーの2つのレイヤーの中にあります。 ValueError
そしてその fmt.Errorf
からのラッパー runValidation
. コードコードは errors.Unwrap
エラーが発生すると、最初の結果になります errors.Unwrap(err)
今は *ValueError
ではなく errUhOh
.
これを修正する1つの方法は、 errUhOh
チェックして、を呼び出すエラーチェックを追加します errors.Unwrap()
両方のレイヤーをアンラップするために2回。 これを追加するには、 main.go
ファイルを作成して更新します main
この変更を含める関数:
...
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := runValidation(num)
if err == errUhOh ||
errors.Unwrap(err) == errUhOh ||
errors.Unwrap(errors.Unwrap(err)) == errUhOh {
fmt.Println("oh no!")
} else if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
今、あなたの main.go
ファイルと使用 go run
プログラムを再度実行するには:
- go run main.go
出力は次のようになります。
Outputvalidating 1... there was an error: run error: value error: that's odd
validating 2... there was an error: run error: value error: uh oh
validating 3... valid!
あなたはそれを見るでしょう、ええと、 errUhOh
特別なエラー処理はまだ機能していません。 を検証する行 2
特別なエラー処理が見られると予想される入力 oh no!
出力はまだデフォルトを示しています there was an error: run error: ...
エラー出力。 これは、 errors.Unwrap
関数は、ラップを解除する方法を知りません ValueError
カスタムエラータイプ。 カスタムエラーをアンラップするには、独自のエラーが必要です。 Unwrap
内部エラーをとして返すメソッド error
価値。 を使用してエラーを作成する場合 fmt.Errorf
とともに %w
以前の動詞、Goは実際にすでにエラーを作成していました Unwrap
メソッドが追加されたので、自分で行う必要はありませんでした。 ただし、独自のカスタム関数を使用しているので、独自の関数を追加する必要があります。
最終的に修正するには errUhOh
エラーケース、開く main.go
を追加します Unwrap
方法 ValueError
それは Err
、内側にラップされたエラーが格納されているフィールド:
...
func (ve *ValueError) Error() string {
return fmt.Sprintf("value error: %s", ve.Err)
}
func (ve *ValueError) Unwrap() error {
return ve.Err
}
...
次に、新しいものを保存したら Unwrap
メソッド、プログラムを実行します。
- go run main.go
出力は次のようになります。
Outputvalidating 1... there was an error: run error: value error: that's odd
validating 2... oh no!
validating 3... valid!
出力は oh no!
のエラー処理 errUhOh
エラーが再び機能しているのは errors.Unwrap
アンラップもできるようになりました ValueError
.
このセクションでは、新しいカスタムを作成しました ValueError
エラーメッセージの一部として、検証プロセスに関する情報を自分自身またはユーザーに提供するためのエラー。 エラーアンラップのサポートも追加しました ValueError
それで errors.Unwrap
ラップされたエラーにアクセスするために使用できます。
ただし、エラー処理は少し不格好になり、保守が難しくなっています。 ラッピングの新しいレイヤーがあるたびに、別のレイヤーを追加する必要がありました errors.Unwrap
それを処理するためのエラーチェックに。 ありがたいことに、 errors.Is
と errors.As
の機能 errors
ラップされたエラーの処理を容易にするパッケージが利用可能です。
ラップされたエラーの処理
新しいものを追加する必要がある場合 errors.Unwrap
プログラムをラップするエラーの潜在的なレイヤーごとに関数呼び出しを行うと、非常に長くなり、保守が困難になります。 このため、2つの追加機能も追加されました。 errors
Go1.13リリースのパッケージ。 これらの関数はどちらも、エラーが他のエラーにどれほど深く含まれていても、エラーを操作できるようにすることで、エラーの処理を容易にします。 The errors.Is
関数を使用すると、特定のセンチネルエラー値がラップされたエラー内のどこかにあるかどうかを確認できます。 The errors.As
関数を使用すると、ラップされたエラー内の任意の場所で特定のタイプのエラーへの参照を取得できます。
でエラー値をチェックする errors.Is
使用する errors.Is
特定のエラーをチェックするには、 errUhOh
手動で行っていたネストされたエラーのアンラップをすべて処理するため、特別なエラー処理ははるかに短くなります。 関数は2つかかります error
パラメータ。最初のパラメータは実際に受け取ったエラーで、2番目のパラメータはチェックしたいエラーです。
クリーンアップするには errUhOh
エラー処理、開いて main.go
ファイルを更新し、 errUhOh
チェックイン main
使用する関数 errors.Is
代わりは:
...
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := runValidation(num)
if errors.Is(err, errUhOh) {
fmt.Println("oh no!")
} else if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
次に、コードを保存し、を使用してプログラムを再実行します go run
:
- go run main.go
出力は次のようになります。
Outputvalidating 1... there was an error: run error: value error: that's odd
validating 2... oh no!
validating 3... valid!
出力は oh no!
エラーメッセージ。これは、エラーチェックが1つしかない場合でも errUhOh
、それはまだエラーチェーンで見つかります。 errors.Is
エラータイプを利用します Unwrap
探しているエラー値、センチネルエラーが見つかるか、エラーが発生するまで、エラーのチェーンを深く掘り下げ続ける方法。 Unwrap
を返すメソッド nil
価値。
使用する errors.Is
エラーラッピングがGoの機能として存在するため、特定のエラーをチェックするための推奨される方法です。 独自のエラー値に使用できるだけでなく、次のような他のエラー値にも使用できます。 sql.ErrNoRows
このチュートリアルで前述したエラー。
でエラータイプを取得する errors.As
に追加された最後の関数 errors
Go1.13のパッケージは errors.As
関数。 この関数は、特定のタイプのエラーへの参照を取得して、それとより詳細に対話する場合に使用されます。 たとえば、 ValueError
以前に追加したカスタムエラーは、で検証されている実際の値へのアクセスを提供します Value
エラーのフィールドですが、最初にそのエラーへの参照がある場合にのみアクセスできます。 これはどこです errors.As
入って来る。 あなたは与えることができます errors.As
次のようなエラー errors.Is
、およびエラーのタイプの変数。 次に、エラーチェーンを調べて、ラップされたエラーのいずれかが提供されたタイプと一致するかどうかを確認します。 一致する場合、エラータイプに渡された変数はエラーで設定されます errors.As
が見つかり、関数は true
. 一致するエラータイプがない場合は、 false
.
使用する errors.As
これで、 ValueError
入力して、エラーハンドラに追加のエラー情報を表示します。 あなたの main.go
最後にもう一度ファイルして、 main
新しいエラー処理ケースを追加する関数 ValueError
-出力するタイプエラー value error
、無効な番号、および検証エラー:
...
func main() {
for num := 1; num <= 3; num++ {
fmt.Printf("validating %d... ", num)
err := runValidation(num)
var valueErr *ValueError
if errors.Is(err, errUhOh) {
fmt.Println("oh no!")
} else if errors.As(err, &valueErr) {
fmt.Printf("value error (%d): %v\n", valueErr.Value, valueErr.Err)
} else if err != nil {
fmt.Println("there was an error:", err)
} else {
fmt.Println("valid!")
}
}
}
上記のコードで、新しい宣言をしました valueErr
変数および使用 errors.As
への参照を取得するには ValueError
中に包まれている場合 err
価値。 としてエラーにアクセスすることによって ValueError
、検証に失敗した実際の値など、タイプが提供する追加のフィールドにアクセスできます。 これは、検証ロジックがプログラムのより深いところで行われ、通常、値にアクセスして、どこで問題が発生したかについてユーザーにヒントを与えることができない場合に役立ちます。 これが役立つ可能性のある別の例は、ネットワークプログラミングを行っていて、net.DNSErrorに遭遇した場合です。 エラーへの参照を取得することにより、エラーが接続できなかったことが原因であるかどうか、またはエラーが接続できたがリソースが見つからなかったことが原因であるかどうかを確認できます。 これを知ったら、さまざまな方法でエラーを処理できます。
見る errors.As
実際には、ファイルを保存し、を使用してプログラムを実行します go run
:
- go run main.go
出力は次のようになります。
Outputvalidating 1... value error (1): that's odd
validating 2... oh no!
validating 3... valid!
今回の出力には、デフォルトは表示されません there was an error: ...
すべてのエラーは他のエラーハンドラーによって処理されているため、メッセージ。 検証用の出力 1
は errors.As
エラーチェックが返されました true
なぜなら value error ...
エラーメッセージが表示されています。 以来 errors.As
関数がtrueを返し、 valueErr
変数は ValueError
アクセスして検証に失敗した値を出力するために使用できます valueErr.Value
.
のために 2
値、出力はまた、 errUhOh
中に包まれています ValueError
ラッパー、 oh no!
特別なエラーハンドラは引き続き実行されます。 これは、を使用する特別なエラーハンドラが原因です。 errors.Is
為に errUhOh
のコレクションで最初に来る if
エラーを処理するステートメント。 このハンドラーは true
の前に errors.As
走っても、特別 oh no!
ハンドラーが実行されます。 の場合 errors.As
あなたのコードでは、 errors.Is
、 oh no!
エラーメッセージは同じになります value error ...
として 1
この場合を除いて、値は出力されます value error (2): uh oh
.
このセクションでは、プログラムを更新して、 errors.Is
に多くの追加の呼び出しを削除する関数 errors.Unwrap
エラー処理コードをより堅牢で将来にわたって利用できるようにします。 あなたも使用しました errors.As
ラップされたエラーのいずれかが ValueError
、次に、値が見つかった場合はその値のフィールドを使用しました。
結論
このチュートリアルでは、を使用してエラーをラップしました %w
動詞をフォーマットし、を使用してエラーをアンラップしました errors.Unwrap
. また、をサポートするカスタムエラータイプを作成しました errors.Unwrap
あなた自身のコードで。 最後に、カスタムエラータイプを使用して、新しいヘルパー関数を調べました errors.Is
と errors.As
.
これらの新しいエラー関数を使用すると、作成または操作するエラーに関するより深い情報を簡単に含めることができます。 また、将来的にエラーが深くネストされた場合でもエラーチェックが機能し続けることを保証するために、コードを将来にわたって保証します。
新しいエラー機能の使用方法の詳細については、Goブログに Go1.13でのエラーの処理に関する投稿があります。 エラーパッケージパッケージのドキュメントにも詳細情報が含まれています。
このチュートリアルは、 DigitalOcean How to Code inGoシリーズの一部でもあります。 このシリーズでは、Goの初めてのインストールから、言語自体の使用方法まで、Goに関する多くのトピックを取り上げています。