2013-03-13 6 views
8

この(IMO)恐ろしい外観のコードをクリーンアップする方法はありますか?複数のエラーをエレガントに処理しますか?

aJson, err1 := json.Marshal(a) 
bJson, err2 := json.Marshal(b) 
cJson, err3 := json.Marshal(c) 
dJson, err4 := json.Marshal(d) 
eJson, err5 := json.Marshal(e) 
fJson, err6 := json.Marshal(f) 
gJson, err4 := json.Marshal(g) 
if err1 != nil { 
    return err1 
} else if err2 != nil { 
    return err2 
} else if err3 != nil { 
    return err3 
} else if err4 != nil { 
    return err4 
} else if err5 != nil { 
    return err5 
} else if err5 != nil { 
    return err5 
} else if err6 != nil { 
    return err6 
} 

特に、私はエラー処理について話しています。一度にすべてのエラーを処理できるのは良いことです。

+0

タイトルはエラー処理に関するものですが、[panic()](http://golang.org/doc)の代わりにそれらを返すようです/effective_go.html#panic)、理由はありますか?また、マーシャル1が失敗した直後に即座に復帰しないこともありますか? – Deleplace

+6

エラーを返すのではなく、なぜpanic()を使用するのですか?あなたがリンクしているドキュメントから: "呼び出し元にエラーを報告する通常の方法は、エラーを余分な戻り値として返すことです。"これは "普通の"ケースだと感じます。 panic()を使用すると正当性が感じられるような重大さのエラーではありません。 (パニック機能の使用例を誤解している場合は、私に修正してください。) –

+3

私はあなたがすべてのgopherが同意するであろうこと、パニックを悪用するのではなく、上のコードサンプルが本当に悪い/あいまいであることを述べるためにupvotedを知っています。すべてをマーシャリングしますが、後で見る最初のエラーだけを返します。しかし、「一度にすべてのエラーを処理する」ことを明確にして、ほとんどコードサンプルを無効にします。繰り返しますが、あなたがやっていることと、パニック/リカバリが適切なことに依存しています。個人的には、私は失敗したマーシャルで直ちにエラーになり、 'cJson'が失敗し、'エラー 'に添付されていることをより具体的に説明します。しかし、それはこれがどこで実行されているのかというコンテキストにも依存します。 – dskinner

答えて

14
var err error 
f := func(dest *D, src S) bool { 
    *dest, err = json.Marshal(src) 
    return err == nil 
} // EDIT: removed() 

f(&aJson, a) && 
    f(&bJson, b) && 
    f(&cJson, c) && 
    f(&dJson, d) && 
    f(&eJson, e) && 
    f(&fJson, f) && 
    f(&gJson, g) 
return err 
+0

編集には6文字以上必要です。触れることはできませんが、 'f 'を宣言するときに'() 'を削除する必要があります。 – dskinner

+0

@dskinner:固定、ありがとう。 – zzzz

13

結果を変数ではなくスライスに置き、初期値を別のスライスに入れて反復し、エラーが発生した場合は反復中に戻ります。

var result [][]byte 
for _, item := range []interface{}{a, b, c, d, e, f, g} { 
    res, err := json.Marshal(item) 
    if err != nil { 
     return err 
    } 
    result = append(result, res) 
} 

あなたも、代わりに2つのスライスを持つ配列を再利用することができます。

var values, err = [...]interface{}{a, b, c, d, e, f, g}, error(nil) 
for i, item := range values { 
    if values[i], err = json.Marshal(item); err != nil { 
     return err 
    } 
} 

もちろん、結果を使用するにはタイプアサーションが必要です。

7

関数を定義します。

func marshalMany(vals ...interface{}) ([][]byte, error) { 
    out := make([][]byte, 0, len(vals)) 
    for i := range vals { 
     b, err := json.Marshal(vals[i]) 
     if err != nil { 
      return nil, err 
     } 
     out = append(out, b) 
    } 
    return out, nil 
} 

エラー処理をどのように機能させるかについては何も言っていません。失敗する、すべて失敗する最初に失敗する?成功を集めるか、それらを投げる?

5

他の回答はあなたの特定の問題では正しいと思いますが、より一般的には、ふさわしいふるまいのライブラリである間にエラー処理を短縮するためにpanicを使用することができます。 (すなわち、ないpanicパッケージ境界を越えてINGの)

検討:値をマーシャリングに問題があるときはいつでも

func mustMarshal(v interface{}) []byte { 
    bs, err := json.Marshal(v) 
    if err != nil { 
     panic(err) 
    } 
    return bs 
} 

func encodeAll() (err error) { 
    defer func() { 
     if r := recover(); r != nil { 
      var ok bool 
      if err, ok = r.(error); ok { 
       return 
      } 
      panic(r) 
     } 
    }() 

    ea := mustMarshal(a)  
    eb := mustMarshal(b) 
    ec := mustMarshal(c) 

    return nil 
} 

このコードはpanicmustMarshalを使用します。しかし、encodeAll関数はパニックからrecoverになり、通常のエラー値として返します。この場合のクライアントはパニックにさらされることはありません。

しかし、これには警告が付いています。このアプローチをどこでも使用することは、慣用的ではありません。それは個々のエラーを特別に処理するのには役立たないが、各エラーを同じように扱うことは多かれ少なかれ、悪化する可能性がある。しかし、それは処理するエラーのトンがあるときにその用途を持っています。例えば、トップレベルのハンドラがさ​​まざまな種類のエラーをキャッチし、エラーの種類に応じて適切にユーザ(またはログファイル)に表示できるWebアプリケーションで、このようなアプローチを使用します。

エラー処理が多いが、慣用的なGoを失い、それぞれのエラーを特別に扱うときに、より簡潔なコードになります。もう一つの側面は、それが何かを防ぐことができるということです実際にパニックからパニックから。 (しかしこれはあなた自身のエラータイプを使って簡単に解決できます)

関連する問題