错误类型

有很多种方法来声明 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. }