Отказ от ответственности: я не знаю, можно ли считать следующий совет «стандартным» или «общепринятым».
Должен ли я использовать для этого перенос ошибок?
Краткий ответ: да (я бы так и сделал).
Перейти на 1.12 и ранее
Что я делаю, когда мне нужно, чтобы мои ошибки передавали какое-то конкретное значение, без использования интерфейса error
, я создаю оболочку, которая реализует интерфейс ошибок - Error() string
-.Эта обертка содержит всю дополнительную информацию, которая мне нужна.
Если вызывающий знает о существовании этой дополнительной информации, он может развернуть ошибку с помощью приведения и найти эту информацию.С дополнительным преимуществом, что неосведомленные абоненты могут просто обрабатывать ошибку как общий error
.
type MyError struct {
DeferredError error
}
// Implements 'error' interface
func (e MyError) Error() string {
// format to string
}
func someFunc() error {
// might return an instance of MyError
}
...
// Caller code
err := someFunc()
if err != nil {
if myErr, ok := err.(*MyError); ok {
// here you can access the wrapped info
fmt.Println(myErr.DeferredError)
} else {
// otherwise handle the error generically
}
}
Go 1.13 вперед
С Go.13 вы можете использовать errors.As
развернуть ошибку.Из официальных документов:
[Метод] As находит первую ошибку в цепочке ошибок, которая соответствует цели, и, если это так, устанавливает target для этого значения ошибки и возвращает true.Цепочка состоит из самой ошибки, за которой следует последовательность ошибок, получаемых при неоднократном вызове Unwrap.
var myErr *MyError
if errors.As(err, &myErr) {
// here you can access the wrapped info
fmt.Println(myErr.DeferredError)
} else {
// otherwise handle the error generically
}
Как говорят в документации, переменная myErr
заполняется как побочный эффект вызова As
.