2013-03-13 35 views
8

Czy istnieje sposób na oczyszczenie tego przerażającego kodu (IMO)?Go - elegancka obsługa wielu błędów?

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 
} 

Mówiąc konkretnie, mówię o obsłudze błędów. Byłoby miło móc obsłużyć wszystkie błędy za jednym zamachem.

+0

Tytuł dotyczy obsługi błędów, ale wydaje się, że zwraca je zamiast [panika()] (http://golang.org/doc) /effective_go.html#panic), czy istnieje powód, dla którego? A także za niezwłoczny powrót, gdy tylko 1 Marshall zawiodł? – Deleplace

+6

Dlaczego miałbym używać opcji paniki() zamiast zwracania błędów? Z dokumentów, do których się przyłączyłeś: "Najczęstszym sposobem zgłoszenia błędu do osoby dzwoniącej jest zwrócenie błędu jako dodatkowej wartości zwracanej." Mam wrażenie, że jest to "zwykły" przypadek. Nie jest to błąd o takiej surowości, że użycie paniki() wydaje się uzasadnione. (Proszę mnie poprawić, jeśli nie rozumiem przypadków użycia funkcji paniki.) –

+3

Wiem, że zostałeś przegłosowany za stwierdzenie, z czym zgadza się każdy Suseł, nie używanie/nadużywanie paniki, ale powyższy przykład kodu jest po prostu zły/niejasny. Marszałek wszystko, ale tylko pierwszy błąd, który widzimy później? Ale potem wyjaśniasz "obsługa wszystkich błędów za jednym razem", głównie unieważniając próbkę kodu. Znowu zależy to od tego, co robisz, i paniki/odzyskiwania mogą być odpowiednie. Osobiście natychmiast pomyłę w przypadku nieudanego marszałka i będę bardziej opisowy, że 'cJson' zawiódł, dołączając do' err'. Ale nawet to zależy od kontekstu, w którym jest to realizowane. – dskinner

Odpowiedz

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

edycja wymaga min 6 znaków, więc nie można jej dotknąć, ale '()' trzeba usunąć, gdy zadeklarujesz 'f'. – dskinner

+0

@dskinner: Naprawiono, dzięki. – zzzz

13

Umieść wynik w plastrze zamiast zmiennych, wstaw wartości początkowe w innym wycinku do iteracji i zwracaj podczas iteracji, jeśli wystąpi błąd.

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) 
} 

Można nawet ponowne tablicę zamiast dwóch plasterków.

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 
    } 
} 

Oczywiście będzie wymagało to potwierdzenia typu, aby użyć wyników.

7

zdefiniować funkcję.

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 
} 

Nie napisałeś nic o tym, jak chcesz, aby obsługa błędów działała. Przegapić jeden, wszystko zawieść? Najpierw się nie uda? Zbieraj sukcesy lub rzucaj nimi?

5

Sądzę, że inne odpowiedzi tutaj są poprawne dla twojego konkretnego problemu, ale ogólnie rzecz biorąc, można użyć panic, aby skrócić obsługę błędów, będąc wciąż dobrze zachowującą się biblioteką. (, To znaczy nie panic nia poza granice pakietu)

Rozważmy:

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 
} 

Kod ten wykorzystuje mustMarshal do panic gdy istnieje problem Organizowanie wartość. Ale funkcja encodeAll z powodu paniki i zwróci ją jako normalną wartość błędu. Klient w tym przypadku nigdy nie jest narażony na panikę.

Ale to pojawia się z ostrzeżeniem: stosowanie tego podejścia wszędzie nie jest idiomatyczne. Może być również gorszy, ponieważ nie nadaje się dobrze do obsługi każdego pojedynczego błędu, ale mniej lub bardziej traktuje każdy błąd tak samo. Ale ma swoje zastosowania, gdy istnieje mnóstwo błędów do obsłużenia. Jako przykład, używam tego rodzaju podejścia w aplikacji internetowej, gdzie program obsługi najwyższego poziomu może przechwytywać różnego rodzaju błędy i wyświetlać je odpowiednio użytkownikowi (lub plikowi logicznemu) w zależności od rodzaju błędu.

Powoduje, że kod terser jest stosowany, gdy występuje wiele błędów, ale z powodu utraty idiomatycznego Go i obsługiwania każdego błędu specjalnie. Inną wadą jest to, że może ona uniemożliwić coś, co może spowodować panikę. (Ale można to rozwiązać w prosty sposób, używając własnego typu błędu.)