In Go worden de interfaces fmt.Stringer en error gebruikt om te beheren hoe een struct waarde naar een string wordt omgezet en hoe het een fout implementeert. Deze interfaces bieden universele manieren voor logging, output en foutafhandeling, waardoor de code flexibeler en begrijpelijker wordt.
Achtergrond van de vraag:
Sinds de eerste versies van Go is de Stringer-interface cruciaal voor een mooi gecontroleerde output van structuren. De error-interface is fundamenteel geworden voor foutafhandeling op alle niveaus van de code.
Probleem:
Programmers krijgen vaak niet-informatieve uitvoer of onverwachte foutmeldingen omdat ze deze methoden niet hebben geïmplementeerd of dit op een onconventionele manier hebben gedaan. Daarnaast kan een onjuiste implementatie leiden tot recursieve uitvoer, panieken en moeilijk leesbare fouten.
Oplossing:
Voorbeeldcode:
package main import "fmt" type User struct { Name string ID int } func (u User) String() string { return fmt.Sprintf("User<%d:%s>", u.ID, u.Name) } type MyError struct { Msg string } func (e MyError) Error() string { return "MyError: " + e.Msg } func main() { u := User{Name: "Bob", ID: 10} fmt.Println(u) // roept String() aan err := MyError{Msg:"fail"} fmt.Println(err) // roept Error() aan }
Belangrijkste kenmerken:
Is het verplicht om String() of Error() als waarde-methoden te implementeren of kunnen we ook pointers gebruiken?
Beide opties zijn toegestaan, maar de implementatie met pointer-receiver en value-receiver beïnvloedt op welke type objecten de methode van toepassing is. Gewoonlijk wordt pointer-receiver gebruikt voor mutabele structuren.
func (u *User) String() string {...}
Kan ik fmt.Sprintf binnen String() of Error() gebruiken?
Ja, maar je moet er voorzichtig mee zijn om eindeloze recursie te voorkomen (bijvoorbeeld, het uitprinten van een structuur die hetzelfde type binnenin heeft, in String()). Het wordt aanbevolen om fmt.Print niet binnen String() te gebruiken als String() intern opnieuw wordt aangeroepen.
func (u User) String() string { return fmt.Sprintf("%v", u.Name) } // veilig
Wat gebeurt er als de methode Error() een lege string retourneert?
Fouten met een lege string worden als geldige error-waarden beschouwd, maar logging verliest dan zijn zin. De error-interface definieert geen gedrag voor een lege string, maar het is gebruikelijk om altijd een informatief bericht te geven.
Een ontwikkelaar print een structuur via %+v, zonder String() te implementeren, en ontvangt daardoor rommelige dumps van velden in de logs.
Voordelen:
Nadelen:
Een teamleider dwingt het team om String() en Error() voor alle publieke structuren te implementeren. Hierdoor wordt de businesslogica centraal behandeld en worden de admin- en debug-logs leesbaar.
Voordelen:
Nadelen: