堅牢なコードは、ユーザー入力の不良、ネットワーク接続の障害、ディスクの障害などの予期しない状況に正しく対応する必要があります。 エラー処理は、プログラムが予期しない状態にあることを識別し、後でデバッグするために診断情報を記録するための手順を実行するプロセスです。
開発者が特殊な構文でエラーを処理する必要がある他の言語とは異なり、Goのエラーは次のタイプの値です。 error
他の値と同様に関数から返されます。 Goでエラーを処理するには、関数が返す可能性のあるこれらのエラーを調べ、エラーが発生したかどうかを判断し、データを保護するための適切なアクションを実行し、エラーが発生したことをユーザーまたはオペレーターに通知する必要があります。
エラーの作成
エラーを処理する前に、まずエラーを作成する必要があります。 標準ライブラリには、エラーを作成するための2つの組み込み関数が用意されています。 errors.New
と fmt.Errorf
. これらの関数はどちらも、後でユーザーに表示できるカスタムエラーメッセージを指定できます。
errors.New
単一の引数を取ります。エラーメッセージを文字列として受け取り、カスタマイズしてユーザーに問題を警告することができます。
次の例を実行して、によって作成されたエラーを確認してください。 errors.New
標準出力に出力:
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("barnacles")
fmt.Println("Sammy says:", err)
}
OutputSammy says: barnacles
使用しました errors.New
標準ライブラリの関数を使用して、文字列を含む新しいエラーメッセージを作成します "barnacles"
エラーメッセージとして。 ここでは、 Goプログラミング言語スタイルガイドが示唆しているように、エラーメッセージに小文字を使用することで慣例に従いました。
最後に、 fmt.Println
エラーメッセージをと組み合わせる関数 "Sammy says:"
.
The fmt.Errorf
関数を使用すると、エラーメッセージを動的に作成できます。 その最初の引数は、次のようなプレースホルダー値を持つエラーメッセージを含む文字列です。 %s
文字列と %d
整数の場合。 fmt.Errorf
このフォーマット文字列に続く引数を、次の順序でこれらのプレースホルダーに補間します。
package main
import (
"fmt"
"time"
)
func main() {
err := fmt.Errorf("error occurred at: %v", time.Now())
fmt.Println("An error happened:", err)
}
OutputAn error happened: Error occurred at: 2019-07-11 16:52:42.532621 -0400 EDT m=+0.000137103
使用しました fmt.Errorf
現在の時刻を含むエラーメッセージを作成する関数。 提供したフォーマット文字列 fmt.Errorf
が含まれています %v
指示するフォーマットディレクティブ fmt.Errorf
フォーマット文字列の後に提供される最初の引数にデフォルトのフォーマットを使用します。 その引数は、によって提供される現在の時刻になります time.Now
標準ライブラリの関数。 前の例と同様に、エラーメッセージを短いプレフィックスと組み合わせて、結果を標準出力に出力します。 fmt.Println
関数。
エラーの処理
通常、前の例のように、このように作成されたエラーが他の目的ですぐに使用されることはありません。 実際には、エラーを作成し、問題が発生したときに関数からエラーを返すのがはるかに一般的です。 その関数の呼び出し元は、 if
エラーが存在したかどうかを確認するステートメントまたは nil
-初期化されていない値。
この次の例には、常にエラーを返す関数が含まれています。 プログラムを実行すると、関数が今回エラーを返しているにもかかわらず、前の例と同じ出力が生成されることに注意してください。 別の場所でエラーを宣言しても、エラーのメッセージは変更されません。
package main
import (
"errors"
"fmt"
)
func boom() error {
return errors.New("barnacles")
}
func main() {
err := boom()
if err != nil {
fmt.Println("An error occurred:", err)
return
}
fmt.Println("Anchors away!")
}
OutputAn error occurred: barnacles
ここでは、という関数を定義します boom()
シングルを返します error
を使用して構築する errors.New
. 次に、この関数を呼び出して、次の行でエラーをキャプチャします err := boom()
. このエラーを割り当てたら、エラーが存在するかどうかを確認します。 if err != nil
条件付き。 ここで、条件は常に次のように評価されます true
、私たちは常に error
から boom()
.
これは常に当てはまるとは限らないため、エラーが存在しない場合にロジックを処理することをお勧めします(nil
)およびエラーが存在する場合。 エラーが存在する場合は、 fmt.Println
前の例で行ったように、プレフィックスとともにエラーを出力します。 最後に、 return
の実行をスキップするステートメント fmt.Println("Anchors away!")
、エラーが発生しなかった場合にのみ実行する必要があるためです。
注: if err != nil
最後の例に示されている構造は、Goプログラミング言語でのエラー処理の主力製品です。 関数がエラーを生成する可能性がある場合は常に、を使用することが重要です if
発生したかどうかを確認するステートメント。 このように、慣用的なGoコードには、当然、最初のインデントレベルに「ハッピーパス」ロジックがあり、2番目のインデントレベルにすべての「サッドパス」ロジックがあります。
Ifステートメントには、関数の呼び出しとそのエラーの処理を要約するために使用できるオプションの割り当て句があります。
次のプログラムを実行して、前の例と同じ出力を確認しますが、今回は化合物を使用します if
ボイラープレートを減らすためのステートメント:
package main
import (
"errors"
"fmt"
)
func boom() error {
return errors.New("barnacles")
}
func main() {
if err := boom(); err != nil {
fmt.Println("An error occurred:", err)
return
}
fmt.Println("Anchors away!")
}
OutputAn error occurred: barnacles
前と同じように、関数があります。 boom()
、常にエラーを返します。 から返されたエラーを割り当てます boom()
に err
の最初の部分として if
声明。 の第2部では if
セミコロンに続くステートメント、その err
その後、変数が使用可能になります。 エラーが存在するかどうかを確認し、以前と同じように短いプレフィックス文字列を使用してエラーを出力します。
このセクションでは、エラーのみを返す関数の処理方法を学習しました。 これらの関数は一般的ですが、複数の値を返す可能性のある関数からのエラーを処理できることも重要です。
値と一緒にエラーを返す
単一のエラー値を返す関数は、多くの場合、データベースへの行の挿入など、ステートフルな変更に影響を与える関数です。 正常に完了した場合に値を返し、その関数が失敗した場合に潜在的なエラーを返す関数を作成することも一般的です。 Goを使用すると、関数が複数の結果を返すことができます。これを使用して、値とエラータイプを同時に返すことができます。
複数の値を返す関数を作成するために、関数のシグニチャの括弧内に各戻り値のタイプをリストします。 たとえば、 capitalize
を返す関数 string
と error
を使用して宣言されます func capitalize(name string) (string, error) {}
. The (string, error)
一部は、この関数が string
と error
、この順序で。
次のプログラムを実行して、両方を返す関数からの出力を確認します。 string
と error
:
package main
import (
"errors"
"fmt"
"strings"
)
func capitalize(name string) (string, error) {
if name == "" {
return "", errors.New("no name provided")
}
return strings.ToTitle(name), nil
}
func main() {
name, err := capitalize("sammy")
if err != nil {
fmt.Println("Could not capitalize:", err)
return
}
fmt.Println("Capitalized name:", name)
}
OutputCapitalized name: SAMMY
定義する capitalize()
文字列(大文字にする名前)を受け取り、文字列とエラー値を返す関数として。 の main()
、私たちは capitalize()
関数から返された2つの値をに割り当てます name
と err
変数の左側をコンマで区切って :=
オペレーター。 この後、私たちは if err != nil
前の例のようにチェックし、を使用してエラーを標準出力に出力します fmt.Println
エラーが存在した場合。 エラーがなかった場合は、印刷します Capitalized name: SAMMY
.
文字列を変更してみてください "sammy"
の name, err := capitalize("sammy")
空の文字列に ("")
エラーが発生します Could not capitalize: no name provided
代わりは。
The capitalize
関数の呼び出し元が空の文字列を提供すると、関数はエラーを返します。 name
パラメータ。 いつ name
パラメータは空の文字列ではありません、 capitalize()
使用 strings.ToTitle
を活用するには name
パラメータと戻り値 nil
エラー値。
この例に従ういくつかの微妙な規則があります。これはGoコードに典型的ですが、Goコンパイラーによって強制されません。 関数がエラーを含む複数の値を返す場合、規則は、 error
最後のアイテムとして。 を返すとき error
複数の戻り値を持つ関数から、慣用的なGoコードは、各非エラー値をゼロ値に設定します。 ゼロ値は、たとえば、文字列の場合は空の文字列です。 0
整数の場合、構造体タイプの場合は空の構造体、および nil
いくつか例を挙げると、インターフェースとポインターのタイプについて。 ゼロ値については、変数と定数に関するチュートリアルで詳しく説明しています。
ボイラープレートを減らす
関数から返す値が多い状況では、これらの規則に従うのは面倒になる可能性があります。 無名関数を使用して、ボイラープレートを減らすことができます。 匿名関数は、変数に割り当てられたプロシージャです。 前の例で定義した関数とは対照的に、それらは宣言した関数内でのみ使用できます。これにより、再利用可能なヘルパーロジックの短い部分として機能するのに最適です。
次のプログラムは、最後の例を変更して、大文字にする名前の長さを含めます。 返す値が3つあるため、エラーの処理は、私たちを支援する匿名関数がないと面倒になる可能性があります。
package main
import (
"errors"
"fmt"
"strings"
)
func capitalize(name string) (string, int, error) {
handle := func(err error) (string, int, error) {
return "", 0, err
}
if name == "" {
return handle(errors.New("no name provided"))
}
return strings.ToTitle(name), len(name), nil
}
func main() {
name, size, err := capitalize("sammy")
if err != nil {
fmt.Println("An error occurred:", err)
}
fmt.Printf("Capitalized name: %s, length: %d", name, size)
}
OutputCapitalized name: SAMMY, length: 5
内部 main()
、ここで、から返された3つの引数をキャプチャします。 capitalize
なので name
, size
、 と err
、 それぞれ。 次に、次のことを確認します capitalize
を返しました error
かどうかを確認することによって err
変数が等しくなかった nil
. これは、によって返される他の値を使用する前に行うことが重要です。 capitalize
、無名関数なので、 handle
、それらをゼロ値に設定できます。 文字列を提供したため、エラーは発生しなかったため "sammy"
、大文字の名前とその長さを出力します。
もう一度、変更してみてください "sammy"
空の文字列に ("")
印刷されたエラーケースを確認するには(An error occurred: no name provided
).
内部 capitalize
、定義します handle
匿名関数としての変数。 単一のエラーを受け取り、の戻り値と同じ順序で同じ値を返します。 capitalize
. handle
それらの値をゼロ値に設定し、 error
最終的な戻り値として引数として渡されます。 これを使用して、で発生したエラーを返すことができます capitalize
を使用して return
呼び出しの前のステートメント handle
とともに error
そのパラメータとして。
それを覚えておいてください capitalize
関数を定義した方法であるため、常に3つの値を返す必要があります。 関数が返す可能性のあるすべての値を処理したくない場合があります。 幸い、割り当て側でこれらの値を使用する方法にはある程度の柔軟性があります。
マルチリターン関数からのエラーの処理
関数が多くの値を返す場合、Goではそれぞれを変数に割り当てる必要があります。 最後の例では、から返された2つの値の名前を指定してこれを行います。 capitalize
関数。 これらの名前はコンマで区切って、左側に表示する必要があります :=
オペレーター。 から返される最初の値 capitalize
に割り当てられます name
変数、および2番目の値( error
)が変数に割り当てられます err
. 場合によっては、エラー値のみに関心があります。 特別なものを使用して、関数が返す不要な値を破棄できます _
変数名。
次のプログラムでは、最初の例を変更しました。 capitalize
空の文字列を渡すことによってエラーを生成する関数 ("")
. このプログラムを実行して、最初の戻り値を破棄することでエラーだけを調べる方法を確認してください。 _
変数:
package main
import (
"errors"
"fmt"
"strings"
)
func capitalize(name string) (string, error) {
if name == "" {
return "", errors.New("no name provided")
}
return strings.ToTitle(name), nil
}
func main() {
_, err := capitalize("")
if err != nil {
fmt.Println("Could not capitalize:", err)
return
}
fmt.Println("Success!")
}
OutputCould not capitalize: no name provided
以内 main()
今回の関数では、大文字の名前を割り当てます( string
最初に返される)アンダースコア変数(_
). 同時に、 error
によって返される capitalize
に err
変数。 次に、エラーがに存在するかどうかを確認します if err != nil
条件付き。 空の文字列を引数としてハードコーディングしたので capitalize
行で _, err := capitalize("")
、この条件は常に次のように評価されます true
. これにより、出力が生成されます "Could not capitalize: no name provided"
への呼び出しによって印刷されます fmt.Println
体内で機能する if
声明。 The return
この後スキップします fmt.Println("Success!")
.
結論
標準ライブラリを使用してエラーを作成する多くの方法と、慣用的な方法でエラーを返す関数を作成する方法を見てきました。 このチュートリアルでは、標準ライブラリを使用してさまざまなエラーを正常に作成することができました errors.New
と fmt.Errorf
機能。 今後のチュートリアルでは、独自のカスタムエラータイプを作成して、より豊富な情報をユーザーに伝える方法を見ていきます。