Doporučené postupy - řešení chyb

Toto je první článek v řadě lekcí, které jsem se naučil během několika let, kdy jsem pracoval s Go ve výrobě. Provozujeme velké množství služeb Go ve výrobě ve společnosti Saltside Technologies (psst, najímám více pozic v Bangalore pro Saltside) a provozuji také vlastní firmu, kde je Go nedílnou součástí.

Pokryjeme širokou škálu témat, velkých i malých.

Prvním předmětem, který jsem chtěl v této sérii pokrýt, je zpracování chyb. Pro nové vývojáře Go to často způsobuje zmatek a nepříjemnost.

Některé pozadí - Chybové rozhraní

Jen tak jsme na stejné stránce. Jak možná víte, chyba v Go je prostě něco, co implementuje rozhraní chyb. Takto vypadá definice rozhraní:

zadejte chybové rozhraní {
    Error () řetězec
}

Jako chybu lze tedy použít cokoli, co implementuje řetězovou metodu Error ().

Kontrola chyb

Použití struktur chyb a kontroly typu

Když jsem začal psát Go, často jsem prováděl řetězcové porovnávání chybových zpráv, abych viděl, o jaký typ chyby jde (ano, trapně na to, abych si vzpomněl, ale někdy se musíš ohlédnout, abys šel vpřed).

Lepším přístupem je použití typů chyb. Takže můžete (samozřejmě) vytvořit struktury, které implementují rozhraní chyb a poté provést porovnání typu v příkazu switch.

Zde je příklad implementace chyby.

typ ErrZeroDivision struct {
    řetězec zprávy
}
func NewErrZeroDivision (řetězec zprávy) * ErrZeroDivision {
    return & ErrZeroDivision {
        zpráva: zpráva,
    }
}
func (e * ErrZeroDivision) Error () řetězec {
    návrat e.message
}

Nyní lze tuto chybu použít takto.

func main () {
    výsledek, err: = divide (1.0, 0.0)
    pokud chybně = = nula {
        přepínač err. (typ) {
        pouzdro * ErrZeroDivision:
            fmt.Println (err.Error ())
        výchozí:
            fmt.Println („Co se právě stalo *?“)
        }
    }
    fmt.Println (výsledek)
}
func divide (a, b float64) (float64, chyba) {
    pokud b == 0,0 {
        návrat 0,0, NewErrZeroDivision („Nelze vydělit nulou“)
    }
    návrat a / b, nula
}

Zde je odkaz Přejít Play pro úplný příklad. Všimněte si vzoru typu přepínání chyb (typ), který umožňuje kontrolovat různé typy chyb, spíše než něco jiného (jako porovnání řetězců nebo něco podobného).

Pomocí balíčku chyb a přímého srovnání

Výše uvedený přístup může být alternativně zpracován pomocí balíčku chyb. Tento přístup je vhodný pro kontrolu chyb v balíčku, kde potřebujete rychlou reprezentaci chyb.

var errNotFound = errors.New ("Item not found")
func main () {
    err: = getItem (123) // To by hodilo errNotFound
    pokud chybně = = nula {
        přepnout err {
        case errNotFound:
            log.Println ("Požadovaná položka nenalezena")
        výchozí:
            log.Println („Došlo k neznámé chybě“)
        }
    }
}

Tento přístup je méně dobrý, pokud potřebujete složitější chybové objekty, např. chybové kódy atd. V takovém případě byste si měli vytvořit svůj vlastní typ implementující rozhraní chyb.

Okamžité zpracování chyb

Někdy narazím na kód, jako je níže (ale obvykle s více chmýří kolem ..):

Chyba func example1 () {
    err: = call1 ()
    návrat err
}

Jde o to, že chyba se neprobíhá okamžitě. Toto je křehký přístup, protože někdo může vložit kód mezi err: = call1 () a návrat err, což by narušilo úmysl, protože to může zastínit první chybu. Dva alternativní přístupy:

// Sbalí návrat a chybu.
func example2 () chyba {
    zpětné volání1 ()
}
// Provádějte explicitní zpracování chyb hned po hovoru.
func example3 () chyba {
    err: = call1 ()
    pokud chybně = = nula {
        návrat err
    }
    návrat nula
}

Oba výše uvedené přístupy jsou se mnou v pořádku. Dosahují stejné věci, která je; pokud někdo potřebuje něco přidat po volání1 (), musí se postarat o zpracování chyb.

To je pro dnešek vše

Zůstaňte naladěni na další článek o Go Best Practices. Být silný :).

func main () {
    err: = readArticle ("Go Best Practices - Error handling")
    pokud chybně = = nula {
        ping ("@ sebdah")
    }
}