Meer informatie over het afhandelen van fouten in Go
Tijdens het schrijven van uw programma's moet u rekening houden met de verschillende manieren waarop uw programma's kunnen mislukken en moet u fouten beheren. Uw gebruikers hoeven geen lange en verwarrende stacktraceringsfout te zien. Het is beter als ze zinvolle informatie zien over wat er mis is gegaan. Zoals u hebt gezien, beschikt Go over ingebouwde functies zoals panic
uitzonderingen recover
, of onverwacht gedrag, in uw programma's. Maar fouten zijn bekende fouten die uw programma's moeten maken om te verwerken.
Go's benadering van foutafhandeling is gewoon een controlestroommechanisme waarbij alleen een if
en een return
instructie nodig zijn. Als u bijvoorbeeld een functie aanroept om informatie op te halen uit een employee
object, wilt u misschien weten of de werknemer bestaat. Go's meningswijze manier voor het verwerken van een dergelijke verwachte fout zou er als volgt uitzien:
employee, err := getInformation(1000)
if err != nil {
// Something is wrong. Do something.
}
U ziet hoe de getInformation
functie de employee
struct retourneert en ook een fout als een tweede waarde. De fout kan zijn nil
. Als de fout zich voordeed nil
, betekent dit succes. Als dat niet nil
zo is, betekent dat dat falen. Een niet-foutnil
wordt geleverd met een foutbericht dat u kunt afdrukken of, bij voorkeur, logboek. Dit is de manier waarop u fouten in Go verwerkt. In de volgende sectie komen enkele andere strategieën aan bod.
U zult waarschijnlijk merken dat foutafhandeling in Go vereist dat u meer aandacht besteedt aan de manier waarop u een fout rapporteert en verwerkt. Dat is precies het punt. Laten we eens kijken naar enkele andere voorbeelden om inzicht te krijgen in go's benadering van foutafhandeling.
We gebruiken het codefragment dat we voor structs hebben gebruikt om verschillende strategieën voor foutafhandeling te oefenen:
package main
import (
"fmt"
"os"
)
type Employee struct {
ID int
FirstName string
LastName string
Address string
}
func main() {
employee, err := getInformation(1001)
if err != nil {
// Something is wrong. Do something.
} else {
fmt.Print(employee)
}
}
func getInformation(id int) (*Employee, error) {
employee, err := apiCallEmployee(1000)
return employee, err
}
func apiCallEmployee(id int) (*Employee, error) {
employee := Employee{LastName: "Doe", FirstName: "John"}
return &employee, nil
}
Van hieruit richten we ons op het wijzigen van de getInformation
, apiCallEmployee
en main
functies om te laten zien hoe u fouten kunt afhandelen.
Strategieën voor foutafhandeling
Wanneer een functie een fout retourneert, wordt dit meestal de laatste retourwaarde. Het is de verantwoordelijkheid van de beller om te controleren of er een fout bestaat en deze te verwerken, zoals u in de vorige sectie hebt gezien. Een algemene strategie is dus om dat patroon te blijven gebruiken om de fout in een subroutine door te geven. Een subroutine (zoals getInformation
in het vorige voorbeeld) kan bijvoorbeeld de fout retourneren aan de beller zonder iets anders te doen, zoals hier:
func getInformation(id int) (*Employee, error) {
employee, err := apiCallEmployee(1000)
if err != nil {
return nil, err // Simply return the error to the caller.
}
return employee, nil
}
U kunt ook meer informatie opnemen voordat u de fout doorgeeft. Voor dat doel kunt u de fmt.Errorf()
functie gebruiken, die vergelijkbaar is met wat we eerder hebben gezien, maar er wordt een fout geretourneerd. U kunt bijvoorbeeld meer context toevoegen aan de fout en nog steeds de oorspronkelijke fout retourneren, zoals:
func getInformation(id int) (*Employee, error) {
employee, err := apiCallEmployee(1000)
if err != nil {
return nil, fmt.Errorf("Got an error when getting the employee information: %v", err)
}
return employee, nil
}
Een andere strategie is het uitvoeren van logica voor opnieuw proberen wanneer fouten tijdelijk zijn. U kunt bijvoorbeeld een beleid voor opnieuw proberen gebruiken om een functie drie keer aan te roepen en twee seconden te wachten, zoals deze:
func getInformation(id int) (*Employee, error) {
for tries := 0; tries < 3; tries++ {
employee, err := apiCallEmployee(1000)
if err == nil {
return employee, nil
}
fmt.Println("Server is not responding, retrying ...")
time.Sleep(time.Second * 2)
}
return nil, fmt.Errorf("server has failed to respond to get the employee information")
}
Ten slotte kunt u, in plaats van fouten naar de console af te drukken, fouten vastleggen en eventuele implementatiedetails van eindgebruikers verbergen. We behandelen logboekregistratie in de volgende module. Laten we nu eens kijken hoe u aangepaste fouten kunt maken en gebruiken.
Herbruikbare fouten maken
Soms neemt het aantal foutberichten toe en wilt u de volgorde behouden. Of misschien wilt u een bibliotheek maken voor veelvoorkomende foutberichten die u opnieuw wilt gebruiken. In Go kunt u de errors.New()
functie gebruiken om fouten te maken en opnieuw te gebruiken in verschillende onderdelen, zoals:
var ErrNotFound = errors.New("Employee not found!")
func getInformation(id int) (*Employee, error) {
if id != 1001 {
return nil, ErrNotFound
}
employee := Employee{LastName: "Doe", FirstName: "John"}
return &employee, nil
}
De code voor de getInformation
functie ziet er beter uit en als u het foutbericht wilt wijzigen, doet u dit op slechts één plaats. U ziet ook dat de conventie het Err
voorvoegsel voor foutvariabelen moet bevatten.
Ten slotte kunt u, wanneer u een foutvariabele hebt, specifieker zijn wanneer u een fout in een aanroeperfunctie verwerkt. Met errors.Is()
de functie kunt u het type fout vergelijken dat u krijgt, zoals deze:
employee, err := getInformation(1000)
if errors.Is(err, ErrNotFound) {
fmt.Printf("NOT FOUND: %v\n", err)
} else {
fmt.Print(employee)
}
Aanbevolen procedures voor foutafhandeling
Wanneer u fouten in Go verwerkt, zijn hier enkele aanbevolen procedures om rekening mee te houden:
- Controleer altijd op fouten, zelfs als u ze niet verwacht. Handel ze vervolgens goed af om te voorkomen dat onnodige informatie aan eindgebruikers beschikbaar wordt.
- Voeg een voorvoegsel toe aan een foutbericht, zodat u de oorsprong van de fout kent. U kunt bijvoorbeeld de naam van het pakket en de functie opnemen.
- Maak zo veel mogelijk herbruikbare foutvariabelen.
- Inzicht in het verschil tussen het gebruik van retourfouten en paniek. Paniek als er niets anders is wat je kunt doen. Als een afhankelijkheid bijvoorbeeld niet gereed is, is het niet logisch dat het programma werkt (tenzij u een standaardgedrag wilt uitvoeren).
- Logboekfouten met zo veel mogelijk details (in de volgende sectie bespreken we hoe ze dit kunnen begrijpen) en druk fouten af die een eindgebruiker kan begrijpen.