Go Best Practices - Tratarea erorilor

Acesta este primul articol dintr-o serie de lecții pe care le-am învățat de-a lungul celor doi ani în care am lucrat cu Go în producție. Executăm un număr mare de servicii Go în producție la Saltside Technologies (psst, angajez pentru mai multe poziții în Bangalore pentru Saltside) și conduc și propria mea afacere în care Go face parte integrantă.

Vom acoperi o gamă largă de subiecte, mari și mici.

Primul subiect pe care am vrut să-l abordez în această serie este tratarea erorilor. Adesea provoacă confuzie și supărare pentru noii dezvoltatori Go.

Unele fundal - Interfața de eroare

Doar așa suntem pe aceeași pagină. După cum poate știți o eroare în Go este pur și simplu orice lucru care implementează interfața de eroare. Așa arată definiția interfeței:

interfață de eroare tip {
    Șir de eroare ()
}

Deci orice poate implementa metoda string (Error) poate fi folosită ca eroare.

Verificarea erorilor

Utilizarea structurilor de eroare și verificarea tipului

Când am început să scriu Go, am făcut deseori comparații șir de mesaje de eroare pentru a vedea care a fost tipul de eroare (da, jenant să te gândești, dar uneori trebuie să te uiți înapoi pentru a merge mai departe).

O abordare mai bună este utilizarea tipurilor de eroare. Așadar, puteți (desigur) crea structuri care implementează interfața de eroare și apoi puteți face comparații de tip într-o instrucțiune switch.

Iată un exemplu de implementare a erorilor.

type ErrZeroDivision struct {
    șir de mesaje
}
func NewErrZeroDivision (șir de mesaje) * ErrZeroDivision {
    return & ErrZeroDivision {
        mesaj: mesaj,
    }
}
func (e * ErrZeroDivision) Eroare () string {
    retur eMessage
}

Acum această eroare poate fi folosită astfel.

func main () {
    rezultat, eroare: = împărțiți (1,0, 0,0)
    if err! = nil {
        comutați eroarea. (tip) {
        cazul * ErrZeroDivision:
            fmt.Println (err.Error ())
        Mod implicit:
            fmt.Println ("Ce s-a întâmplat h * tocmai s-a întâmplat?")
        }
    }
    fmt.Println (rezultat)
}
func divide (a, b float64) (float64, eroare) {
    dacă b == 0,0 {
        return 0.0, NewErrZeroDivision ("Nu se poate împărți la zero")
    }
    returnați a / b, nil
}

Iată linkul Go Play pentru exemplul complet. Observați tiparul de eroare a comutatorului (tip), ceea ce face posibilă verificarea diferitelor tipuri de erori, în loc de altceva (cum ar fi compararea șirurilor sau ceva similar).

Folosind pachetul de erori și comparație directă

Abordarea de mai sus poate fi tratată alternativ folosind pachetul de erori. Această abordare este recomandabilă pentru verificările de eroare din pachet unde aveți nevoie de o reprezentare rapidă a erorilor.

var errNotFound = errors.New ("Articolul nu a fost găsit")
func main () {
    err: = getItem (123) // Aceasta ar arunca errNotFound
    if err! = nil {
        comutați eroare {
        caz errNotFound:
            log.Println ("Articol solicitat nu a fost găsit")
        Mod implicit:
            log.Println ("A apărut o eroare necunoscută")
        }
    }
}

Această abordare este mai puțin bună atunci când ai nevoie de obiecte de eroare mai complexe, de ex. coduri de eroare etc. În acest caz, ar trebui să creați propriul tip care implementează interfața de eroare.

Tratarea imediată a erorilor

Uneori întâlnesc cod precum cel de mai jos (dar de obicei cu mai mult puf în jurul ..):

func example1 () eroare {
    err: = apel1 ()
    reveni eroare
}

Ideea este că eroarea nu este tratată imediat. Aceasta este o abordare fragilă, deoarece cineva poate introduce cod între err: = call1 () și returnarea err, ceea ce ar încălca intenția, deoarece aceasta poate umbri prima eroare. Două abordări alternative:

// Colaps returnare și eroare.
func example2 () eroare {
    retur apel1 ()
}
// Efectuați o eroare explicită la gestionarea imediat după apel.
func example3 () eroare {
    err: = apel1 ()
    if err! = nil {
        reveni eroare
    }
    întoarce nil
}

Ambele abordări de mai sus sunt în regulă. Ei realizează același lucru, care este; dacă cineva trebuie să adauge ceva după apel1 () trebuie să aibă grijă de gestionarea erorilor.

Asta este totul pentru ziua de azi

Fii la curent cu următorul articol despre Go Best Practices. Merge tare :).

func main () {
    err: = readArticle ("Mergeți cele mai bune practici - gestionarea erorilor")
    if err! = nil {
        ping ( "@ sebdah")
    }
}