W Go interfejsy fmt.Stringer i error są używane do zarządzania tym, jak wartość struktury jest konwertowana na łańcuch oraz jak implementuje błąd. Te interfejsy zapewniają uniwersalne sposoby logowania, wyświetlania i obsługi błędów, czyniąc kod bardziej elastycznym i zrozumiałym.
Historia pytania:
Od pierwszych wersji Go interfejs Stringer stał się kluczowy dla eleganckiego kontrolowania wyjścia struktur. Interfejs error okazał się fundamentem dla obsługi błędów na wszystkich poziomach kodu.
Problem:
Często programiści otrzymują mało informacyjne wyjście lub niespodziewane komunikaty błędów, ponieważ nie zaimplementowali tych metod lub zrobili to w nietypowy sposób. Niewłaściwa implementacja może również prowadzić do rekurencyjnych wyjść, panik i trudnych do odczytania błędów.
Rozwiązanie:
Przykład kodu:
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) // wywołuje String() err := MyError{Msg:"fail"} fmt.Println(err) // wywołuje Error() }
Kluczowe cechy:
Czy konieczne jest implementowanie String() lub Error() jako metody- wartości czy można używać wskaźników?
Oba warianty są dozwolone, ale implementacja na wskaźniku i wartości wpływa na to, na jakich typach obiektów metoda zadziała. Zwykle wykorzystuje się wskaźnik dla struktur mutowalnych.
func (u *User) String() string {...}
Czy można używać fmt.Sprintf wewnątrz String() lub Error()?
Tak, ale należy uważać, aby nie wywołać nieskończonej rekurencji (na przykład, wyjście struktury z tym samym typem wewnątrz String()). Zaleca się unikanie użycia fmt.Print wewnątrz String(), jeśli wewnętrznie ponownie wywoływane będzie String().
func (u User) String() string { return fmt.Sprintf("%v", u.Name) } // bezpiecznie
Co się stanie, jeśli metoda Error() zwraca pusty łańcuch?
Błędy z pustym łańcuchem są postrzegane jako ważne wartości error, ale w takim przypadku logowanie traci sens. Interfejs error nie definiuje zachowania przy pustym łańcuszku, ale powszechnie przyjęte jest zawsze dostarczanie informacyjnego komunikatu.
Programista wyprowadza strukturę przez %+v, nie implementując String(), w rezultacie otrzymując śmieciowe zrzuty pól w logach.
Zalety:
Wady:
Team lead zmusza zespół do zaimplementowania String() i Error() dla wszystkich publicznych struktur. W wyniku tego logika biznesowa obsługuje błędy centralnie, a admin panel i logi debugowe stają się czytelne.
Zalety:
Wady: