Go Best Practices - Foutafhandeling

Dit is het eerste artikel in een reeks lessen die ik heb geleerd in de paar jaar dat ik met Go in productie heb gewerkt. We hebben een behoorlijk aantal Go-services in productie bij Saltside Technologies (PSST, ik neem meerdere functies aan in Bangalore voor Saltside) en ik heb ook mijn eigen bedrijf waar Go een integraal onderdeel is.

We behandelen een breed scala aan onderwerpen, groot en klein.

Het eerste onderwerp dat ik in deze serie wilde behandelen, is foutafhandeling. Het veroorzaakt vaak verwarring en ergernis voor nieuwe Go-ontwikkelaars.

Enige achtergrond - de foutinterface

Zodat we op dezelfde pagina zijn. Zoals je misschien weet, is een fout in Go gewoon alles dat de foutinterface implementeert. Dit is hoe de interfacedefinitie eruit ziet:

type error interface {
    Fout () tekenreeks
}

Dus alles dat de String () -reeksmethode implementeert, kan als een fout worden gebruikt.

Controleren op fouten

Foutstructuren en typecontrole gebruiken

Toen ik Go begon te schrijven, heb ik vaak stringvergelijkingen van foutmeldingen gemaakt om te zien wat het fouttype was (ja, beschamend om aan te denken, maar soms moet je terugkijken om vooruit te gaan).

Een betere aanpak is om fouttypen te gebruiken. U kunt dus (natuurlijk) structs maken die de foutinterface implementeren en vervolgens typevergelijkingen uitvoeren in een schakelinstructie.

Hier is een voorbeeld van een implementatie van fouten.

type ErrZeroDivision struct {
    berichtstring
}
func NewErrZeroDivision (berichtenreeks) * ErrZeroDivision {
    retour & ErrZeroDivision {
        bericht: bericht,
    }
}
func (e * ErrZeroDivision) Error () string {
    retourneer e.message
}

Nu kan deze fout als volgt worden gebruikt.

func main () {
    resultaat, err: = delen (1.0, 0.0)
    if err! = nil {
        schakelaar fout (type) {
        case * ErrZeroDivision:
            fmt.Println (err.Error ())
        standaard:
            fmt.Println ("Wat is er h * net gebeurd?")
        }
    }
    fmt.Println (resultaat)
}
func divide (a, b float64) (float64, fout) {
    if b == 0.0 {
        retourneer 0.0, NewErrZeroDivision ("Kan niet delen door nul")
    }
    retourneer a / b, nihil
}

Hier is de Go Play-link voor het volledige voorbeeld. Let op het patroon van de schakelaarfout (type), waardoor het mogelijk is om op verschillende fouttypen te controleren in plaats van op iets anders (zoals tekenreeksvergelijking of iets dergelijks).

Gebruik van het foutenpakket en directe vergelijking

De bovenstaande benadering kan ook worden afgehandeld met behulp van het foutenpakket. Deze aanpak is aan te bevelen voor foutcontroles binnen het pakket waarbij u een snelle foutrepresentatie nodig hebt.

var errNotFound = errors.New ("Item niet gevonden")
func main () {
    err: = getItem (123) // Dit zou errNotFound gooien
    if err! = nil {
        schakelaar fout {
        case errNotFound:
            log.Println ("Gevraagde item niet gevonden")
        standaard:
            log.Println ("Onbekende fout opgetreden")
        }
    }
}

Deze aanpak is minder goed als u complexere foutobjecten nodig hebt met b.v. foutcodes enz. In dat geval moet u uw eigen type maken dat de foutinterface implementeert.

Onmiddellijke foutafhandeling

Soms kom ik code zoals de onderstaande tegen (maar meestal met meer pluis in de buurt ..):

func example1 () error {
    err: = call1 ()
    retour fout
}

Het punt hier is dat de fout niet onmiddellijk wordt afgehandeld. Dit is een kwetsbare benadering, omdat iemand code kan invoegen tussen err: = call1 () en de retourfout, waardoor de bedoeling zou worden verbroken, omdat dit de eerste fout kan overschaduwen. Twee alternatieve benaderingen:

// Vouw de retour en de fout samen.
func example2 () error {
    terugbellen1 ()
}
// Voer expliciete foutafhandeling direct na het gesprek uit.
func example3 () error {
    err: = call1 ()
    if err! = nil {
        retour fout
    }
    retour nul
}

Beide bovenstaande benaderingen vind ik prima. Ze bereiken hetzelfde, namelijk; als iemand na call1 () iets moet toevoegen, moet deze zorgen voor de foutafhandeling.

Dat is alles voor vandaag

Houd het volgende artikel over Go Best Practices in de gaten. Word sterk :).

func main () {
    err: = readArticle ("Go Best Practices - Foutafhandeling")
    if err! = nil {
        ping ( "@ sebdah")
    }
}