Go1.23中errors.Is新增了一个nil检查

摘要

在 Go 1.23 中,errors.Is 函数中添加了一个 nil 检查,可以让处理错误时写出可读性更好的代码。

正文

在 Go 1.23 之前,errors.Is 函数中没有检查 err 参数是否为 nil,当时的代码如下:

1
2
3
4
5
6
7
8
func Is(err, target error) bool {
 if target == nil {
  return err == target
 }

 isComparable := reflectlite.TypeOf(target).Comparable()
 return is(err, target, isComparable)
}

因此,要检查函数返回的错误是否为特定的错误,需要像下面这样使用嵌套的 if 语句:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
err := foo()
if err != nil {
  if errors.Is(err, errorBar) {
    // handle error Bar
  }
  if errors.Is(err, errorBar2) {
    // handle error Bar2
  }
  // handle other errors
}

虽然这段代码看着没有任何问题,当然实际也没有任何问题,只是它看着没有那么的优雅。

在 Go 1.23 中,你可以去掉 if 语句的嵌套,只需要一层代码来处理错误检查。这主要是因为现在的 errors.Is 函数做了一些修改,现在代码如下:

1
2
3
4
5
6
7
8
func Is(err, target error) bool {
 if err == nil || target == nil {
  return err == target
 }

 isComparable := reflectlite.TypeOf(target).Comparable()
 return is(err, target, isComparable)
}

以前,errors.Is 只会检查 target 是否等于 nil,现在还会检查 err 是否为 nil。因此,我们可以把错误检查代码改进如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
err := foo()
if errors.Is(err, errorBar) {
  // handle error Bar
}
if errors.Is(err, errorBar2) {
  // handle error Bar2
}
if err != nil {
  // handle other errors
}

我相信这种方法比之前的代码更好,虽然只做了一点改变,但与旧的代码相比少了一层嵌套,可读性明显提升。
尽管最初编写代码时看起来很简单,但之前的代码中嵌套的 if 语句可能在以后阅读时更加令人困惑和复杂。
另外可读性较好的代码应该优先考虑正常路径,减少不必要的嵌套深度。

结论

在阅读代码时,我注意到 errors.is 函数缺乏空值检查,这导致了诸多错误和不便,对此我感到相当不满。幸运的是,在 Go 1.23 版本中,errors.is 得到了改进。尽管一些 Go 开发者对此持有不同观点,但这一增强无疑将极大地促进我们编写更清晰、更易于维护的代码,同时提升代码的可读性,并降低其复杂性。