Go のエラーハンドリング#
作成日:2021 年 12 月 7 日午後 6 時 24 分
タグ:Golang
公開日:2021 年 12 月 29 日
問題#
Go 言語の非常に厄介な問題の 1 つは、エラーハンドリングです。
すべての関数はエラーを返す必要があり、返すたびに判断を行う必要があります。これにより、コアの呼び出しコードのロジックの大部分がエラーハンドリングに費やされ、そして毎回処理するコードはまったく同じです。これは冗長で意味のないものであり、以前に Python のシンプルな哲学に触れた私にとっては非常に苦痛です。
しかし、この問題は最適化できないわけではありません。もちろん、より良い可読性はある程度のパフォーマンスを犠牲にする必要がありますが、Go 自体のパフォーマンスは非常に優れており、可読性は問題ではありません。複雑なロジックを処理する場所では、この方法を使用して Go のエラーハンドリングコードを減らすことは、利益が損失を上回ると言えます。
解決策#
以下にデモを示します。
// まずコードを書いてみましょう
// 文字列の判定を処理する呼び出し関数
func A(a, b string) (string, error) {
if a != b {
return "", errors.NewErr("info")
}
return a, nil
}
// intの判定を処理する呼び出し関数
func B(a, b int) (int, error) {
if a != b {
return 0, errors.NewErr("info")
}
return a, nil
}
// メイン関数
func Main(a, b string, c, d int) error {
// 通常の方法では、呼び出すたびにエラーを処理する必要があります
res, err := A(a, b)
if err != nil {
return err
}
res, err = B(c, d)
if err != nil {
return err
}
// ここでは100回の呼び出しを省略します
return nil
}
上記のコードからわかるように、メイン関数が他の関数を 1 回呼び出すたびに、1 回の処理が必要であり、1 行の呼び出しに対して 3 行のエラーハンドリングが必要です。呼び出しのロジックは完全にエラーハンドリングに埋もれてしまっています。
解決策は、呼び出される関数を少し変更し、それらもエラーを処理できるようにすることです。メイン関数のエラーハンドリングを各関数に移動することで、関数の再利用回数が多いほど、節約できるコードも多くなります。さらに、可読性も大幅に向上します。
以下にデモを示します。
func A(a, b string, err error) (string, error) {
// errと以下のロジックを追加しました
// errorパラメータを受け取り、空でない場合は返します
if err != nil {
return "", err
}
if a != b {
return "", errors.NewErr("info")
}
return a, nil
}
func B(a, b int) (int, error) {
if err != nil {
return 0, nil
}
if a != b {
return 0, errors.NewErr("info")
}
return a, nil
}
func Main(a, b string, c, d int) error {
res, err := A(a, b, nil)
res, err = B(c, d, err)
// ここでは100回の呼び出しを省略します
// 呼び出し回数に関係なく、エラーハンドリングは1回だけ書く必要があることがわかります
if err != nil {
return err
}
return nil
}
反省#
このスタイルは、すべての場所で私たちのエラーハンドリングの問題を解決するために使用できるのでしょうか?
私はこれが万能ではないと考えています。少なくとも次のいくつかの問題があります。
- 1 つの関数でエラーが発生すると、後続の関数は実際には実行されませんが、関数呼び出しとパラメータの渡しは存在するため、ある程度のパフォーマンスの損失が必ず発生します。最適なパフォーマンスを追求する部分では、この方法は適していません。
- この方法は、デバッグの位置特定をやや困難にする可能性があります。すべてのロジックが実行された後にエラーメッセージが返されるため、具体的なエラーの場所を直感的に表示することはできません。もちろん、解決策もあります。エラーメッセージに関数のスタック情報を追加することができますが、やや手間がかかります。
結論#
このエラーハンドリング方法は、コードの可読性を向上させることができますが、微小なパフォーマンスの影響を与え、デバッグの難易度をわずかに増加させます。しかし、複雑なコードに対しては、このわずかな損失を高い可読性に交換することは、私個人としては価値があると考えています。