• 错误类型

    错误类型

    有很多种方法来声明 errors:

    • errors.New 声明简单的静态字符串错误信息
    • fmt.Errorf 声明格式化的字符串错误信息
    • 为自定义类型实现 Error() 方法
    • 通过 "pkg/errors".Wrap 包装错误类型

    返回错误时,请考虑以下因素来作出最佳选择:

    • 这是一个不需要其他额外信息的简单错误吗?如果是,使用error.New
    • 客户需要检测并处理此错误吗?如果是,那应该自定义类型,并实现 Error() 方法。
    • 是否是在传递一个下游函数返回的错误?如果是,请查看error 封装部分。
    • 其他,使用 fmt.Errorf

    如果客户需要检测错误,并且是通过 errors.New 创建的一个简单的错误,请使用var 声明这个错误类型。

    BadGood
    1. // package foo
    2. func Open() error {
    3. return errors.New("could not open")
    4. }
    5. // package bar
    6. func use() {
    7. if err := foo.Open(); err != nil {
    8. if err.Error() == "could not open" {
    9. // handle
    10. } else {
    11. panic("unknown error")
    12. }
    13. }
    14. }
    1. // package foo
    2. var ErrCouldNotOpen = errors.New("could not open")
    3. func Open() error {
    4. return ErrCouldNotOpen
    5. }
    6. // package bar
    7. if err := foo.Open(); err != nil {
    8. if err == foo.ErrCouldNotOpen {
    9. // handle
    10. } else {
    11. panic("unknown error")
    12. }
    13. }

    如果你有一个错误需要客户端来检测,并且你想向其添加更多信息(例如,它不是一个简单的静态字符串),那么应该声明一个自定义类型。

    BadGood
    1. func open(file string) error {
    2. return fmt.Errorf("file %q not found", file)
    3. }
    4. func use() {
    5. if err := open(); err != nil {
    6. if strings.Contains(err.Error(), "not found") {
    7. // handle
    8. } else {
    9. panic("unknown error")
    10. }
    11. }
    12. }
    1. type errNotFound struct {
    2. file string
    3. }
    4. func (e errNotFound) Error() string {
    5. return fmt.Sprintf("file %q not found", e.file)
    6. }
    7. func open(file string) error {
    8. return errNotFound{file: file}
    9. }
    10. func use() {
    11. if err := open(); err != nil {
    12. if _, ok := err.(errNotFound); ok {
    13. // handle
    14. } else {
    15. panic("unknown error")
    16. }
    17. }
    18. }

    直接将自定义的错误类型设为导出需要特别小心,因为这意味着他们已经成为包的公开 API 的一部分了。更好的方式是暴露一个匹配函数来检测错误。

    1. // package foo
    2. type errNotFound struct {
    3. file string
    4. }
    5. func (e errNotFound) Error() string {
    6. return fmt.Sprintf("file %q not found", e.file)
    7. }
    8. func IsNotFoundError(err error) bool {
    9. _, ok := err.(errNotFound)
    10. return ok
    11. }
    12. func Open(file string) error {
    13. return errNotFound{file: file}
    14. }
    15. // package bar
    16. if err := foo.Open("foo"); err != nil {
    17. if foo.IsNotFoundError(err) {
    18. // handle
    19. } else {
    20. panic("unknown error")
    21. }
    22. }