simon

simon

github

Goのエラー処理

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. 1 つの関数でエラーが発生すると、後続の関数は実際には実行されませんが、関数呼び出しとパラメータの渡しは存在するため、ある程度のパフォーマンスの損失が必ず発生します。最適なパフォーマンスを追求する部分では、この方法は適していません。
  2. この方法は、デバッグの位置特定をやや困難にする可能性があります。すべてのロジックが実行された後にエラーメッセージが返されるため、具体的なエラーの場所を直感的に表示することはできません。もちろん、解決策もあります。エラーメッセージに関数のスタック情報を追加することができますが、やや手間がかかります。

結論#

このエラーハンドリング方法は、コードの可読性を向上させることができますが、微小なパフォーマンスの影響を与え、デバッグの難易度をわずかに増加させます。しかし、複雑なコードに対しては、このわずかな損失を高い可読性に交換することは、私個人としては価値があると考えています。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。