ПрограммированиеGo разработчик

Как устроена работа с ошибками (errors) в Go: какие существуют подходы к обработке, почему ошибки принято возвращать, а не бросать, и как реализовать собственные типы ошибок?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Go есть стандартный подход к обработке ошибок через возврат значения типа error из функции. В отличие от языков с исключениями, в Go исключения (panic, recover) используются только для truly exceptional situations; для бизнес-логики следует использовать обычную ошибку.

Классический способ:

func doSomething() error { // ... return nil // или errors.New("some error") } err := doSomething() if err != nil { // обработка ошибки }

Вы можете создавать собственные типы ошибок для более подробного анализа:

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"}

Типичная проверка конкретной ошибки:

if myErr, ok := err.(*MyError); ok && myErr.Code == 404 { // обработка ошибки 404 }

Вопрос с подвохом.

Можно ли сравнивать error с nil с помощью == в Go?

Ответ: Да, можно и нужно, чтобы проверить отсутствие ошибки. Но важно помнить: если error был обернут или создан с помощью ошибок, содержащих nil внутри структур, то сравнение может быть неверным. Например:

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

Ошибку делают потому, что интерфейс не равен nil, если его тип не nil, даже если значение внутри nil.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

Микросервис занимался логированием ошибок через сравнение на nil. Один из разработчиков возвращал типизированную ошибку вроде (*MyError)(nil), не замечая, что это не эквивалентно nil (интерфейс не nil). В результате обработка ошибок перестала работать, и множество ложных алертов попали в метрики.


История

В проекте вместо передачи ошибки по цепочке, использовали panic для всех ошибок, считая это "чистым" способом. Это привело к падению приложения при каждом невалидном вводе пользователя, т.к. panic/recover не предназначены для потоковой бизнес-логики.


История

Ошибка была обернута несколько раз с помощью fmt.Errorf("some: %w", err), а при проверке на конкретный тип ошибки делали обычное сравнение, вместо использования errors.Is и errors.As. Это приводило к тому, что бизнес-логика не реагировала на кастомные типы ошибок, хотя те реально присутствовали внутри обернутой ошибки.