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 つの関数でエラーが発生すると、後続の関数は実際には実行されませんが、関数呼び出しとパラメータの渡しは存在するため、ある程度のパフォーマンスの低下があります。最適なパフォーマンスを追求する場合、この方法は適していません。
- この方法は、デバッグの位置特定をやや困難にします。すべてのロジックが実行された後にエラーメッセージが返されるため、具体的なエラーの場所を直感的に表示することはできません。もちろん、解決策もあります。エラーメッセージに関数のスタック情報を追加することで、問題を解決できますが、少し手間がかかります。
結論#
このエラーハンドリング方法は、コードの可読性を向上させることができますが、わずかなパフォーマンスの影響を与え、デバッグの難易度をわずかに増加させます。ただし、複雑なコードに対しては、このわずかな損失を高い可読性に交換することは、個人的には価値があると考えています。