【问题标题】:Does Go have standard Err variables?Go 有标准的 Err 变量吗?
【发布时间】:2015-05-11 21:21:12
【问题描述】:

刚开始使用 Golang。我认为声明一个错误变量并在错误结构中使用它来确定出了什么问题是惯用的,就像在strconv.go 中所做的那样。在那里,ErrRangeErrSyntax 被声明,并且在适当的时候,当它们返回时,对它们的引用存储在 NumError 结构中。我认为原因是因为那时可以将NumError中存储的错误引用的地址与ErrRangeErrSyntax变量进行比较,以确定返回的错误类型。

是否有“标准”这样声明的错误类型?例如,在 Java 中,您有 java.lang.IllegalArgumentException 之类的东西。例如,是否有 ErrArgumentErrUnsupportedOperation 可以在我自己的代码中使用,而不是创建每次都表示相同含义的新错误变量?

【问题讨论】:

    标签: go


    【解决方案1】:

    包作者有几种常见的惯用方式来返回错误。

    1. 固定错误变量,通常命名为Err…

      var (
              ErrSomethingBad = errors.New("some string")
              ErrKindFoo      = errors.New("foo happened")
      )
      
    2. 错误类型,通常命名为…Error

      type SomeError struct {
           // extra information, whatever might be useful to callers
           // (or for making a nice message in `Error()`)
           ExtraInfo int
      }
      type OtherError string
      
      func (e SomeError) Error() string { /* … */ }
      func (e OtherError) Error() string {
              return fmt.Sprintf("failure doing something with %q", string(e))
      }
      

      对于 Go 1.13 及更高版本,您可能还希望实现 Unwrap() error 方法以与 errors.Unwrap 一起使用。

    3. 根据需要特设 errors.New 值。

      func SomepackageFunction() error {
              return errors.New("not implemented")
      }
      
    4. 使用标准包中定义的错误。通常仅限于io.EOF等小集合;在大多数情况下,最好通过上面的方法 1 创建自己的。

      func SomeFunc() error {
              return io.EOF
      }
      

      请注意,有时在实现接口时(例如将Read 方法变为io.Reader最好使用匹配错误(或接口规范的“必需” )。

    5. 制作net.Error等接口:

      type Error interface {
          error
          Timeout() bool   // Is the error a timeout?
          Temporary() bool // Is the error temporary?
      }
      
    6. 使用 Go 1.13 或更高版本,返回带有简单上下文的现有错误(对于更复杂的上下文,使用带有 Unwrap() 方法的自定义错误类型):

      func SomepackageFunction() error {
          err := somethingThatCanFail()
          if err != nil {
                  return fmt.Errorf("some context: %w", err)
          }
      }
      

      注意新的(to Go 1.13)格式化动词%w,它包装了提供的错误,以便调用者可以使用errors.Unwraperror.Is 来处理它。

    您通常会混合使用所有这些方法。

    如果您认为任何包的用户曾经想要测试特定错误,则首选第一个、第二个和第五个。 它们允许:

    err := somepkg.Function()
    if err == somepkg.ErrSomethingBad {
            // …
    }
    // or for an error type, something like:
    if e, ok := err.(somepkg.SomeError); ok && e.ExtraInfo > 42 {
            // use the fields/methods of `e` if needed
    }
    

    对于 Go 1.13 或更高版本,上面可以改为:

    err := somepkg.Function()
    if errors.Is(err, somepkg.ErrSomethingBad) {
            // …
    }
    // or for an error type, something like:
    var someErr somepkg.SomeError
    if errors.As(err, &someErr) && someErr.ExtraInfo > 42 {
            // use the fields/methods of `someErr` if needed
    }
    

    不同之处在于错误将根据需要展开。

    第五种方式(只是第二种方式的扩展)允许检查错误的行为/类型(或使用 Go 1.13 的 errors.As):

    if e, ok := err.(net.Error); ok && e.Timeout() {
            // it's a timeout, sleep and retry
    }
    

    第三种方式的问题是它没有给包的用户留下理智的方式来测试它。 (测试err.Error() 返回的字符串的内容不是一个好主意)。 但是,对于您不希望任何人想要测试的错误,这很好。

    进一步阅读:

    【讨论】:

      【解决方案2】:

      不,没有。只需提供可理解的错误而不是通用错误。 IllegalArgument 传输什么信息?不多,不够。

      【讨论】:

      • 通常,即使我正在编写具有类似 Java 的异常类别的 Python,我的 intent 也是为了处理我知道我想要一个特殊路径的特定错误(比如调用一些外部 API 的网络错误),或者捕获任何东西(例如,记录和中止)。因此,潜在的特殊雪花错误值大多有效。 os 包确实提供了对自己的错误进行分类的函数(例如,os.IsPermission(err error) bool),但那是另一回事。
      【解决方案3】:

      如您所见,特定软件包使用了特定错误。例如,在database/sql 包中,它们定义:

      var ErrNoRows = errors.New("sql: no rows in result set")
      

      因此,如果您执行QueryRow(将错误推迟到Scan),然后执行Scan,您可以这样做

      if  err := row.Scan(&data); err != nil && err != sql.ErrNoRows {
          //something actually went wrong
      } else if err != nil {
          //no results
      } else {
          //we found it
      }
      

      os/exec 拥有var ErrNotFound = errors.New("executable file not found in $PATH")

      encoding/json 有一个type UnmarshalTypeError,它只是一个实现error 接口的类型。

      所以不,虽然没有“标准错误集”,但您可以(并且很可能应该)拥有可以重复使用的特定错误变量。

      您可以使用自己的 errorMsgs 包,在其中可以重复使用常见错误:

      err := doSomething(); if err != nil {
          switch err {
              case errorMsgs.IllegalArgument:
                 //do something
              case errorMsgs.CouldNotConnect:
                 //do something else
           }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-09-12
        • 1970-01-01
        • 1970-01-01
        • 2018-11-06
        • 1970-01-01
        • 1970-01-01
        • 2015-10-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多