74

私は現在goを学んでおり、コードの一部は次のようになります。

a, err := doA()
if err != nil {
  return nil, err
}
b, err := doB(a)
if err != nil {
  return nil, err
}
c, err := doC(b)
if err != nil {
  return nil, err
}
... and so on ...

エラーチェックにほとんどの行がかかるため、これは私には少し間違っているように見えます。エラー処理を行うためのより良い方法はありますか? リファクタリングでこれを回避できますか?

更新:すべての回答に感謝します。私の例では、doB は a に依存し、doC は b に依存することに注意してください。したがって、この場合、提案されているほとんどのリファクタリングは機能しません。他の提案はありますか?

4

6 に答える 6

50

これは一般的な苦情であり、いくつかの回答があります。

以下にいくつかの一般的なものを示します。

1 - 悪くない

これは、これらの苦情に対する非常に一般的な反応です。コードに数行の余分なコードがあるという事実は、実際にはそれほど悪いことではありません。ちょっと安っぽいタイピングで、読む側ではとても扱いやすいです。

2 - それは実際には良いことです

これは、これらの余分な行を入力して読み取ることは、実際にはその時点でロジックがエスケープされる可能性があり、その前の行に配置したリソース管理を元に戻す必要があることを非常によく思い出させるという事実に基づいています. これは通常、暗黙的な方法でロジックの流れを壊す可能性がある例外と比較して提起され、開発者は代わりに隠されたエラー パスを常に念頭に置いておく必要があります。少し前に、私はこれについてより詳細な暴言をここに書きました。

3 - パニック/回復を使用する

panic特定の状況では、既知の型で使用し、recoverパッケージ コードが公開される直前に使用して、適切なエラーに変換し、代わりにそれを返すことで、その作業の一部を回避できます。この手法は、(un)マーシャラーなどの再帰的ロジックをアンロールするために最も一般的に見られます。

私は個人的に、これをあまり乱用しないように努めています。なぜなら、私はポイント 1 と 2 とより密接に関連しているからです。

4 - コードを少し整理する

状況によっては、繰り返しを避けるためにロジックを少し再編成することができます。

些細な例として、これは次のとおりです。

err := doA()
if err != nil {
    return err
}
err := doB()
if err != nil {
    return err
}
return nil

次のように編成することもできます。

err := doA()
if err != nil {
    return err
}
return doB()

5 - 名前付きの結果を使用する

名前付きの結果を使用して、return ステートメントから err 変数を削除する人もいます。ただし、これを行うことはお勧めしません。これは、節約がほとんどできず、コードの明瞭さが低下し、1 つまたは複数の結果が救済の return ステートメントの前に定義されたときにロジックが微妙な問題を起こしやすくなるためです。

6 - if 条件の前にステートメントを使用する

以下のコメントで Tom Wilde がよく思い出したようにif、Goのステートメントは条件の前に単純なステートメントを受け入れます。だからあなたはこれを行うことができます:

if err := doA(); err != nil {
    return err
}

これは優れた Go イディオムであり、頻繁に使用されます。

いくつかの特定のケースでは、明確にするためにステートメントを独立させるために、この方法でステートメントを埋め込むことを避けたいと考えていますが、これは微妙で個人的なことです.

于 2013-09-12T18:53:01.340 に答える
6

名前付きの戻りパラメーターを使用して、物事を少し短くすることができます

遊び場リンク

func doStuff() (result string, err error) {
    a, err := doA()
    if err != nil {
        return
    }
    b, err := doB(a)
    if err != nil {
        return
    }
    result, err = doC(b)
    if err != nil {
        return
    }
    return
}

Go でプログラミングをしばらくすると、すべての関数のエラーをチェックしなければならないことで、その関数がうまくいかなかった場合の実際の意味と対処方法を考えるようになることに気付くでしょう。

于 2013-09-12T19:00:36.740 に答える
4

これらのエラーチェックがいくつかあるような状況が何度も繰り返される場合は、次のようなユーティリティ関数を自分で定義できます。

func validError(errs ...error) error {
    for i, _ := range errs {
        if errs[i] != nil {
            return errs[i]
        }
    }
    return nil
}

これにより、エラーの 1 つを選択して、nil 以外のエラーがある場合に返すことができます。

使用例 (プレイ中のフルバージョン):

x, err1 := doSomething(2)
y, err2 := doSomething(3)

if e := validError(err1, err2); e != nil {
    return e
}

もちろん、これは関数が互いに依存していない場合にのみ適用できますが、これはエラー処理を要約するための一般的な前提条件です。

于 2013-09-12T20:20:27.443 に答える
0

おそらく、呼び出しサイトでエラーを処理しないことに慣れているため、間違っているように見えます。これは go では非常に慣用的ですが、慣れていない場合は定型文のように見えます。

ただし、いくつかの利点があります。

  1. エラーが生成されたサイトで、このエラーを処理する適切な方法を考える必要があります。
  2. コードを読むと、コードが中断して早期に戻るすべてのポイントを簡単に確認できます。

本当にバグがある場合は、for ループと無名関数を使用して創造的になることができますが、それはしばしば複雑で読みにくくなります。

于 2013-09-12T18:58:14.600 に答える