zoukankan      html  css  js  c++  java
  • Golang 如何使用正确使用error?

    有很多种方法来声明 errors:

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

    1.如何自定义错误类型?

        客户需要检测并处理此错误吗?如果是,那应该自定义类型,并实现 Error() 方法。

    type errNotFound struct {
    }
    func (fe *errNotFound) Error() string {
        return "找不到文件"
    }
    //模拟错误
    func openFile() ([]byte, error) {
        return nil, &errNotFound{}
    }
    func main() {
        _, err := openFile()
        if err != nil {
            fmt.Println(err)
        }
    }
    # 找不到文件

         当你需要捕获一些错误的具体信息,往往也需要通过自定义的方式,比如下面这个例子捕获哪个文件打开错误了

    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("test.txt"); err != nil {
            if err, ok := err.(errNotFound); ok {
                fmt.Println(err)
            } else {
                panic("unknown error")
            }
        }
    }
    func main() {
        use()
    }
    # file "test.txt" not found

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

    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")
      }
    }

    2.当你需要定义一个简单的错误信息,可以用errors.New也可以用fmt.Errorf

         如果项目中仅仅是为了打日志,就可以直接用了,如果项目中需要捕获具体的错误并进行对应的处理,规范的做法是先声明这个错误类型

    比较优秀的做法是这样做,切记不要通过Error()判断字符串这种方式去识别错误

    var ErrCouldNotOpen = errors.New("could not open")
    func Open() error {
      return ErrCouldNotOpen
    }
    if err := foo.Open(); err != nil {
      if err == ErrCouldNotOpen {
        // handle
      } else {
        panic("unknown error")
      }
    }

        errors.New本质上也可以通过自定义模拟实现

    func New(text string) error {
        return &errorString{text}
    }
    type errorString struct {
        s string
    }
    func (e *errorString) Error() string {
        return e.s
    }
    func test() error {
        return New("测试NewError")
    }
    func main() {
        err := test()
        fmt.Printf("%v", err)
    }
    # 测试NewError

    3. 实际项目中,大量的if err != nil { log.Error(...)....}充斥在代码中

        一、可读性较差

        二、大量重复日志,给运维带来压力

      三、很难一次定位错误位置

        可以通过 "pkg/errors"包中提供的 Wrap、WithMessage、Cause等方法解决这个问题

        Wrap 可以包装信息加堆栈信息、WithMessage只包装信息、Cause可以找到原始错误

    func inner() error {
        return errors.Wrap(sql.ErrNoRows, "inner failed")
    }
    func out() error {
        return errors.WithMessage(inner(), "out failed")
    }
    func main() {
        err := out()
        if err != nil {
            fmt.Printf("%+v
    ", err)
        }
        # sql: no rows in result set
        # inner failed
        # ...test.go:11
        # ...test.go:14
        # ...test.go:17
        # ...
        # out failed
        if errors.Cause(err) == sql.ErrNoRows {
            # true
        }
    }

    参考:

    https://qcrao.com/2019/09/18/golang-error-break-through/

    https://zhuanlan.zhihu.com/p/98152645

  • 相关阅读:
    Java魔法堂:String.format详解
    Postgresql 正则表达式
    Linux下安装LAMP(Apache+PHP+MySql)和禅道
    Redis 启动警告错误解决[转]
    Postgresql: UUID的使用
    在Linux下安装RabbitMQ
    Python的包管理工具Pip
    在Linux CentOS 6.6上安装RedisLive
    [转]在Linux CentOS 6.6上安装Python 2.7.9
    在Linux上rpm安装运行Redis 3.0.4
  • 原文地址:https://www.cnblogs.com/peterleee/p/13890893.html
Copyright © 2011-2022 走看看