2013-04-20 18 views
9

Sono nuovo per andare e trovare la gestione degli errori per essere estremamente prolisso. Ho letto il ragionamento e per lo più sono d'accordo, ma ci sono alcuni punti in cui sembra che ci sia più codice per gestire gli errori di quanto non faccia effettivamente il lavoro. Ecco un esempio (forzato), in cui metto "Hello world!" in cat e leggi e stampa l'output. Fondamentalmente ogni riga ha altri tre per gestire l'errore, e io non sto nemmeno gestendo nulla.Gestione errori multipli in 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 
} 

Esiste un modo idiomatico e pulito per gestirlo? Mi sento come se mi mancasse qualcosa.

+1

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

risposta

7

Chiaramente, dobbiamo gestire eventuali errori; non possiamo semplicemente ignorarli.

Per esempio, cercando di rendere l'esempio meno artificiale,

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

uscita:

Hello world! 
Hello world! 

Error Handling and Go

In Vai, la gestione degli errori è importante. Il design della lingua e le convenzioni ti incoraggiano a verificare esplicitamente gli errori in cui si verificano (come distinto dalla convenzione in altre lingue di lancio delle eccezioni ea volte catturandoli). In alcuni casi ciò rende il codice Go dettagliato.

+1

Questo non risolve il problema di un sacco di codice ripetuto. Vedi la [domanda utente2303335 collegata a] (http://stackoverflow.com/questions/15397419/go-handling-multiple-errors-elegantly) per una soluzione migliore. – amon

+4

@amon: le risposte alla domanda collegata sono tutte insoddisfacenti. Leggi l'esemplare [Vai codice sorgente] (https://code.google.com/p/go/source/browse/) per le librerie Go principali. In base alla progettazione, il codice Go verifica la presenza di errori. Riscrivi il codice nella domanda tu stesso e mostraci cosa ti viene in mente. – peterSO

0

Per idiomatica, consultare la risposta peterSO s', che comincia a toccare sul tema degli errori tornare, e questo può essere preso ulteriormente avvolgendo gli errori con un po' po 'più di informazioni relative al contesto della chiamata all'interno applicazione.

Ci possono essere casi in cui viene eseguito iterativi oltre un'operazione potrebbero giustificare qualcosa di più generalizzata con alcuni esempi insolitamente creative nel seguente link, ma come ho commentato su tale questione, è stato un esempio di codice cattivo per esaminare: Go — handling multiple errors elegantly?

Indipendentemente dall'esempio che hai, questo non è altro che un one-off in main, quindi consideralo tale se stai solo cercando di gingillarti come si farebbe in una console python interattiva.

+1

Il mio esempio era una funzione principale una tantum per brevità. Nel mio codice attuale, non posso semplicemente ignorare gli errori. –

+0

La brevità può cambiare completamente la risposta. Se il codice è realmente destinato a main, allora usare 'log.Fatal' sarebbe adatto quando si incontra un errore. Se questo fa parte di una chiamata RPC, avvolgere l'errore e allegare altri utili bit di informazione (dovremmo fallire? Dovremmo fornire quello che abbiamo?) Sarebbe adatto. Il punto centrale del mio commento: 'restituire gli errori, e questo può essere preso ulteriormente avvolgendo gli errori con alcune informazioni aggiuntive relative al contesto della chiamata all'interno dell'applicazione. Il codice di esempio che ignora errs è un ripensamento e un promemoria che si può fare quella. – dskinner

-3

In una situazione come questa, di solito appiattisco un po '.

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 
} 

Ancora brutto, ma almeno è meno verticale, e otteniamo un po 'di allineamento.

Non posso dire che questo aderisca a qualsiasi tipo di convenzione, ma è molto più facile leggere IMO.

+12

La prima applicazione di 'gofmt' sul tuo codice distruggerà la tua formattazione. – peterSO

0

Ho appena scritto alcune centinaia di righe in Go, quindi non ho il titolo di indicare alcun modo idiomatico. Tuttavia, in caso di passaggi ripetuti di call-and-check-error, trovo che il codice sia un po 'più facile da scrivere e da leggere anche se cancello la logica: invece di controllare la condizione per uscire (err! = Nil), Controllo la condizione per continuare (err == nil), come mostrato di seguito.
Questo può essere fatto, se si dispone di un modo univoco di gestire l'errore, indipendentemente dall'errore, come tornare al chiamante o stamparlo/registrarlo. Lo svantaggio di questo approccio è che non è possibile dichiarare implicitamente le variabili con: =, perché avrebbero lo scope del blocco if in cui sono assegnate.

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 
}