Концепт обработки ошибок в Go довольно интересен в первую очередь тем, что ошибка в Go это просто произвольный объект поддерживающий интерфейс с функцией
if val == 42 {
return fmt.Errorf("42 is not allowed")
}
// normal workflow
return nil
}
...
err := foo(1)
if err != nil {
// do some error handling
}
// normal workflow
И в принципе тут всё хорошо до тех пор, пока не возникает необходимость в понимании того, что же именно случилось в
ErrFortyTwo = fmt.Errorf("42 is not allowed")
)
func foo(val int) error {
if val == 42 {
return ErrFortyTwo
}
// normal workflow
return nil
}
...
err := foo(1)
if err == ErrFortyTwo {
// do some error handling for ErrConstant
}
// normal workflow
Но рано или поздно наступает следующая стадия: я хочу иметь более информативную ошибку, куда можно добавить произвольный текст известный только на этапе исполнения. В моем случае нужно было получить даже немного больше:
- Компонент, в котором произошла ошибка;
- Код ошибки;
- Произвольное текстовое описание для ошибки.
Итоговое решение оказалось довольно удобным (по меркам мира Go, разработчики на C++ может и будут плеваться). Для начала стоит защититься от неверных кодов ошибок и за одно облегчить вывод ошибки на экран/в лог.
type Code int
//go:generate stringer -type=Component
//go:generate stringer -type=Code
const (
ErrOne Code = iota
ErrTwo
ComponentOne Component = iota
ComponentTwo
)
Я воспользовался сторонней утилитой
error
Code() Code
Component() Component
}
type ErrorDescription struct {
component Component
code Code
message string
}
func (e *ErrorDescription) Error() string {
return fmt.Sprintf("%s | %s: %s", e.component, e.code, e.message)
}
func (e *ErrorDescription) Code() Code {
return e.code
}
func (e *ErrorDescription) Component() Component {
return e.component
}
Работа с
if val == 42 {
return ErrorDescription{ComponentOne, ErrTwo, "42 is not allowed"}
}
// normal workflow
return nil
}
...
err := foo(1)
if errCmpBase, ok := err.(CompBaseError); ok && errCmpBase.Component() == ComponentOne && errCmpBase.Code() == ErrTwo {
// do some error handling for error from ComponentOne with code ErrTwo
}
// normal workflow
Благодаря утилите
Не сказать что я счастлив, но свои плюсы в подходе Go к обработке ошибок безусловно есть, особенно в сочетании с линтерами не позволяющими игнорировать ошибки.