2013-04-20 23 views
9

Jestem nowy, aby przejść i znalezienie obsługi błędów jest bardzo szczegółowe. Czytałem rozumowanie i większość się z tym zgadzam, ale jest kilka miejsc, w których wydaje się, że jest więcej kodów do obsługi błędów niż faktyczne wykonanie pracy. Oto (wymyślony) przykład, w którym potokuję "Witaj, świecie!" do kota i odczytać i wydrukować wynik. Zasadniczo każda linia ma jeszcze trzy, aby poradzić sobie z błędem, a ja tak naprawdę nie radzę sobie z niczym.Obsługa wielu błędów w go

package main 

import "fmt" 
import "io" 
import "io/ioutil" 
import "os/exec" 


func main() { 
    cmd := exec.Command("cat", "-") 
    stdin, err := cmd.StdinPipe() 
    if err != nil { 
     return 
    } 
    stdout, err := cmd.StdoutPipe() 
    if err != nil { 
     return 
    } 
    err = cmd.Start() 
    if err != nil { 
     return 
    } 
    _, err = io.WriteString(stdin, "Hello world!") 
    if err != nil { 
     return 
    } 
    err = stdin.Close(); 
    if err != nil { 
     return 
    } 
    output, err := ioutil.ReadAll(stdout) 
    if err != nil { 
     return 
    } 
    fmt.Println(string(output)) 
    return 
} 

Czy istnieje idiomatyczny, czysty sposób radzenia sobie z tym? Po prostu czuję, że czegoś brakuje.

+1

http://stackoverflow.com/questions/15397419/go-handling-multiple-errors-elegantly?rq=1 –

Odpowiedz

7

Oczywiście musimy poradzić sobie z błędami; nie możemy ich po prostu zignorować.

Na przykład, starając się uczynić przykład mniej sztuczny,

package main 

import (
    "fmt" 
    "io" 
    "io/ioutil" 
    "os" 
    "os/exec" 
) 

func piping(input string) (string, error) { 
    cmd := exec.Command("cat", "-") 
    stdin, err := cmd.StdinPipe() 
    if err != nil { 
     return "", err 
    } 
    stdout, err := cmd.StdoutPipe() 
    if err != nil { 
     return "", err 
    } 
    err = cmd.Start() 
    if err != nil { 
     return "", err 
    } 
    _, err = io.WriteString(stdin, input) 
    if err != nil { 
     return "", err 
    } 
    err = stdin.Close() 
    if err != nil { 
     return "", err 
    } 
    all, err := ioutil.ReadAll(stdout) 
    output := string(all) 
    if err != nil { 
     return output, err 
    } 
    return output, nil 
} 

func main() { 
    in := "Hello world!" 
    fmt.Println(in) 
    out, err := piping(in) 
    if err != nil { 
     fmt.Println(err) 
     os.Exit(1) 
    } 
    fmt.Println(out) 
} 

wyjściowa:

Hello world! 
Hello world! 

Error Handling and Go

w Go, obsługa błędów jest ważna. Projekt języka i konwencje zachęcają do wyraźnego sprawdzenia, czy nie występują błędy w miejscu, w którym wystąpiły (w odróżnieniu od konwencji w innych językach wyrzucania wyjątków, a czasami ich przechwycenia). W niektórych przypadkach powoduje to, że kod Go jest pełny.

+1

to nie rozwiązuje problemu partii powtarzanego kodu. Zobacz [pytanie user2303335 powiązane z] (http://stackoverflow.com/questions/15397419/go-handling-multiple-errors-elegantly) dla lepszego rozwiązania. – amon

+4

@amon: Odpowiedzi na połączone pytanie są niezadowalające. Zapoznaj się z przykładowym kodem źródłowym Go (https://code.google.com/p/go/source/browse/) dla podstawowych bibliotek Go. Zgodnie z projektem, kod Go sprawdza błędy. Przepisz sam kod w pytaniu i pokaż nam, co wymyśliłeś. – peterSO

0

Dla idiomatyczne, patrz odpowiedź peterSO „s, który zaczyna dotykać na temat powrotu błędów, a to może być kontynuowana przez owinięcie błędy z jakimś dodatkowym bitem informacji związanych z kontekstem rozmowy w swoim podanie.

Mogą wystąpić przypadki, gdzie iteracyjne biegnie ponad operacji mogłyby uzasadnić coś bardziej uogólniony z kilkoma niezwykle kreatywnych przykładów w poniższym linku, ale jak skomentował to pytanie, to był zły przykład kodu do zbadania: Go — handling multiple errors elegantly?

Niezależnie od tego, patrząc wyłącznie na przykład, który masz, to nic więcej niż jednorazowy w main, więc traktuj go jak taki, jeśli po prostu chcesz się pomylić, jak można zrobić w interaktywnej konsoli python.

package main 

import (
    "fmt" 
    "io" 
    "io/ioutil" 
    "os/exec" 
) 

func main() { 
    cmd := exec.Command("cat", "-") 
    stdin, _ := cmd.StdinPipe() 
    stdout, _ := cmd.StdoutPipe() 

    cmd.Start() 
    io.WriteString(stdin, "Hello world!") 

    stdin.Close(); 
    output, _ := ioutil.ReadAll(stdout) 

    fmt.Println(string(output)) 
} 
+1

Mój przykład był jednorazową główną funkcją ze względu na zwięzłość. W moim prawdziwym kodzie nie mogę po prostu zignorować błędów. –

+0

Zwięzłość może całkowicie zmienić odpowiedź. Jeśli kod jest rzeczywiście przeznaczony do głównego, użycie "log.Fatal" będzie odpowiednie w przypadku napotkania błędu. Jeśli jest to część wywołania RPC, zawijanie błędu i dołączanie innych użytecznych bitów informacji (gdybyśmy się nie powiedzieli, gdybyśmy zapewnili to, co mamy?) Byłyby odpowiednie. Główny temat mojego komentarza: "zwracanie błędów, a to można zrobić dalej, zawijając błędy z dodatkowymi bitami informacji związanych z kontekstem połączenia w aplikacji." Przykładowy kod ignorujący błędy to refleksja i przypomnienie, że można zrobić że. – dskinner

-3

W takiej sytuacji zwykle po prostu spłaszczam to.

func myFunc() (err error) { 
    cmd := exec.Command("cat", "-") 

    stdin, err := cmd.StdinPipe();     if err != nil { return } 
    stdout, err := cmd.StdoutPipe();     if err != nil { return } 

     err = cmd.Start();       if err != nil { return } 
    _, err = io.WriteString(stdin, "Hello world!"); if err != nil { return } 
     err = stdin.Close();       if err != nil { return } 
    o, err := ioutil.ReadAll(stdout);    if err != nil { return } 

    fmt.Println(string(o)) 
    return 
} 

Nadal brzydki, ale przynajmniej jest mniej wertykalny i otrzymujemy pewne wyrównanie.

Nie mogę powiedzieć, że jest to zgodne z jakąkolwiek konwencją, ale o wiele łatwiej jest przeczytać IMO.

+12

Pierwsza aplikacja 'gofmt' do twojego kodu zniszczy twoje formatowanie. – peterSO

0

Właśnie napisałem kilkaset wierszy w Go, więc nie jestem zatytułowany, aby wskazać jakiś idiomatyczny sposób. Jednak w przypadku powtarzających się kroków sprawdzania wywołań i połączeń, stwierdzam, że kod jest nieco łatwiejszy do zapisu i do odczytu, gdybym odwrócił logikę: zamiast sprawdzania warunku wychodzenia (err! = Nil), Sprawdzam warunek kontynuacji (err == zero), jak pokazano poniżej.
Można to zrobić, jeśli masz unikalny sposób na obsługę błędu, bez względu na to, który błąd jest, np. Powrót do rozmówcy lub drukowanie/rejestrowanie. Wadą tego podejścia jest to, że nie można domyślnie zadeklarować zmiennych za pomocą: =, ponieważ mają one zakres bloku if, do którego są przypisane.

func main() { 
    var output []byte 
    var stdin io.WriteCloser 
    var stdout io.Reader 

    cmd := exec.Command("cat", "-") 

    stdin, err := cmd.StdinPipe() 

    if err == nil { 
     stdout, err = cmd.StdoutPipe() 
    } 

    if err == nil { 
     err = cmd.Start() 
    } 

    if err == nil { 
     _, err = io.WriteString(stdin, "Hello world!") 
    } 

    if err == nil { 
    output, err = ioutil.ReadAll(stdout) 
    } 

    if err == nil { 
    err = stdin.Close(); 
    } 

    if err == nil { 
      fmt.Println(string(output)) 
    } else { 
     fmt.Println(string(err.Error())) // Error handling goes here 
    } 

    return 
}