如何创建一个自定义错误,它是另一个自定义错误的子类型,比如继承?

How to create a custom error that's a subtype of another custom error, like inherited?

对于给定的自定义错误类型:

type CustomError struct {
    // Err optionally wraps the original error.
    Err error `json:"-"`

    // Human readable message.
    Message string `json:"message" validate:"required,gte=3"`

    // StatusCode is a valid HTTP status code, e.g.: 404.
    StatusCode int `json:"-"`
}

实现了Error() stringUnwrap() error接口,还有一个工厂:

func NewCustomError(m string, s int, e error) *CustomError {}

如何基于 CustomType 创建另一个“类型”- 我们将其称为 FailedTo 以解决诸如 “无法创建 X” 之类的错误默认情况下:

除此之外,还有一个,例如 FailedToCreateSomething,在某种程度上......

func createSomething() error {
    return FailedToCreateSomething(errors.New("File is busy"))
}

errCreateSomething := createSomething()

...errCreateSomethingFailedToCreateSomething 的类型,也是 FailedToCustomError?

的类型

让我们从示例中提炼出它的本质。

package customerror

import (
    "errors"
)

type CustomError struct {
    Aux string
    Err error
}

func (cE *CustomError) Error() string { /*...*/ }
func (err *CustomError) Unwrap() error { return err.Err }

func NewFailedToError(aux string, err error) error {
    return &CustomError{ Aux: aux, Err: err }
}

var FailedToWriteToFile = NewFailedToError("write to file", nil)

我们现在可以进入要点:

// Some function just for demonstration.
func WriteToFile() error {
    // Something caused some error..
    errSome := errors.New("Failed to open file")

    // How can I set the `err` value of `FailedToWriteToFile` to `errSome`
    // without setting that for all `FailedToWriteToFile` instances (pointer)?
    // while still making it of `FailedToWriteToFile` type (errors.Is(errSome, FailedToWriteToFil))?
    return FailedToWriteToFile
}

让我们将这个问题重新定义为如何使用包含 errors.Is(errSome, FailedToWriteToFil) 的新消息制作 errSome

我们可以查看 errors.Is:

的文档

Is reports whether any error in err's chain matches target.

The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.

An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.

An error type might provide an Is method so it can be treated as equivalent to an existing error. For example, if MyError defines

func (m MyError) Is(target error) bool { return target == fs.ErrExist }

then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for an example in the standard library.

这给了我们两条路。路径之一是将 FailedToWriteToFile 放在 Unwrap 链上。如果我们使用 Err 字段指向 FailedToWriteToFile,则 CustomError 有足够的字段,例如

&CustomError{Aux: "Failed to open file", Err: FailedToWriteToFile}

Unwrap() 在此 == 到 FailedToWriteToFile。如果您试图捕获来自另一个来源的错误值,FWIW Aux 可能是错误类型的字段。只要 Unwrap() 链最终导致 FailedToWriteToFile

另一个途径是在CustomError 上定义一个Is 谓词。有很多可能的谓词可供选择。一个简单的方法是所有 *CustomError 都被认为是等价的。那将是:

func (e *CustomError) Is(target error) bool {
  _, ok := target.(*CustomError)
  return ok
}