programowanieProgramista Go

Jak działa obsługa błędów (errors) w Go: jakie podejścia do przetwarzania istnieją, dlaczego błędy są zwracane, a nie wyrzucane, i jak zrealizować własne typy błędów?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Go istnieje standardowe podejście do obsługi błędów poprzez zwracanie wartości typu error z funkcji. W przeciwieństwie do języków z wyjątkami, w Go wyjątki (panic, recover) są używane tylko w naprawdę wyjątkowych sytuacjach; w logice biznesowej należy używać zwykłych błędów.

Klasyczny sposób:

func doSomething() error { // ... return nil // lub errors.New("some error") } err := doSomething() if err != nil { // obsługa błędu }

Możesz tworzyć własne typy błędów dla bardziej szczegółowej analizy:

type MyError struct { Code int Message string } func (e *MyError) Error() string { return fmt.Sprintf("(%d) %s", e.Code, e.Message) } err := &MyError{404, "not found"}

Typowa kontrola konkretnego błędu:

if myErr, ok := err.(*MyError); ok && myErr.Code == 404 { // obsługa błędu 404 }

Pytanie z podstępem.

Czy można porównywać error z nil za pomocą == w Go?

Odpowiedź: Tak, można i trzeba, aby sprawdzić brak błędu. Ale ważne jest, aby pamiętać: jeśli błąd został owinięty lub utworzony z użyciem błędów zawierających nil wewnątrz struktur, porównanie może być nieprawidłowe. Na przykład:

var err error = (*MyError)(nil) fmt.Println(err == nil) // false!

Błąd nie jest równy nil, jeśli jego typ nie jest nil, nawet jeśli wewnętrzna wartość to nil.

Przykłady rzeczywistych błędów z powodu braku znajomości subtelności tematu.


Historia

Mikrousługa zajmowała się logowaniem błędów poprzez porównanie z nil. Jeden z programistów zwracał typizowany błąd, np. (*MyError)(nil), nie zauważając, że to nie jest równoważne nil (interfejs nie jest nil). W rezultacie obsługa błędów przestała działać, a wiele fałszywych alertów trafiło do metryk.


Historia

W projekcie zamiast przekazywania błędu w łańcuchu używano panic dla wszystkich błędów, uważając to za "czysty" sposób. Doprowadziło to do awarii aplikacji przy każdym niewalidnym wejściu użytkownika, ponieważ panic/recover nie są przeznaczone do strumieniowej logiki biznesowej.


Historia

Błąd był owinięty kilka razy z użyciem fmt.Errorf("some: %w", err), a przy sprawdzaniu na konkretny typ błędu stosowano zwykłe porównanie, zamiast używać errors.Is i errors.As. To prowadziło do tego, że logika biznesowa nie reagowała na niestandardowe typy błędów, mimo że te rzeczywiście znajdowały się wewnątrz owiniętego błędu.