Существуют различные варианты объявления ошибок:
- errors.New для ошибок с простыми статическими строками
- fmt.Errorf для форматированных строк ошибок
- настраиваемые типы, реализующие метод Error()
- обернутые ошибки с помощью "github.com/pkg/errors".Wrap
При возврате ошибок учитывайте следующее, чтобы определить лучший выбор:
- Это простая ошибка, не требующая дополнительной информации? Если да, то errors.New должно хватить.
- Нужно ли клиентам обнаруживать и обрабатывать эту ошибку? В таком случае следует использовать настраиваемый тип и реализовать метод Error().
- Распространяете ли вы ошибку, возвращаемую нижестоящей функцией? Если да, используете обертывание ошибок.
- В противном случае используйте fmt.Errorf.
Если клиенту необходимо обнаружить ошибку, и вы создали простую ошибку, используя errors.New, используйте var для ошибки.
Менее удачный вариант:
// package foo
func Open() error {
return errors.New("could not open")
}
// package bar
func use() {
if err := foo.Open(); err != nil {
if err.Error() == "could not open" {
// обработка
} else {
panic("unknown error")
}
}
}
Более удачный вариант:
// package foo
var ErrCouldNotOpen = errors.New("could not open")
func Open() error {
return ErrCouldNotOpen
}
// package bar
if err := foo.Open(); err != nil {
if err == foo.ErrCouldNotOpen {
// обработка
} else {
panic("unknown error")
}
}
Более удачный вариант с версии Go 1.13:
// package foo
var ErrCouldNotOpen = errors.New("could not open")
func Open() error {
return ErrCouldNotOpen
}
// package bar
if err := foo.Open(); err != nil {
if errors.Is(err, foo.ErrCouldNotOpen) {
// обработка
} else {
panic("unknown error")
}
}
Если у вас есть ошибка, которую, возможно, потребуется обнаружить клиентам, и вы хотите добавить к ней дополнительную информацию (например, это не статическая строка), вам следует использовать настраиваемый тип.
Менее удачный вариант:
func open(file string) error {
return fmt.Errorf("file %q not found", file)
}
func use() {
if err := open("testfile.txt"); err != nil {
if strings.Contains(err.Error(), "not found") {
// обработка
} else {
panic("unknown error")
}
}
}
Более удачный вариант:
type errNotFound struct {
file string
}
func (e errNotFound) Error() string {
return fmt.Sprintf("file %q not found", e.file)
}
func open(file string) error {
return errNotFound{file: file}
}
func use() {
if err := open("testfile.txt"); err != nil {
if _, ok := err.(errNotFound); ok {
// обработка
} else {
panic("unknown error")
}
}
}
Будьте осторожны с прямым экспортом пользовательских типов ошибок, поскольку они становятся частью общедоступного API пакета. Лучше вместо этого предоставлять функции сопоставления для проверки ошибки.
// package foo
type errNotFound struct {
file string
}
func (e errNotFound) Error() string {
return fmt.Sprintf("file %q not found", e.file)
}
func IsNotFoundError(err error) bool {
_, ok := err.(errNotFound)
return ok
}
func Open(file string) error {
return errNotFound{file: file}
}
// package bar
if err := foo.Open("foo"); err != nil {
if foo.IsNotFoundError(err) {
// handle
} else {
panic("unknown error")
}
}
Читайте также:
Комментариев нет:
Отправить комментарий