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 没有进行空值检查时,我在阅读代码时遇到了许多错误和困难,对于 errors.is 中缺乏空值检查我感到非常不满意。
现在 errors.is 在 go 1.23 中已经得到优化,尽管一些 gophers 可能存在哲学分歧,但这个增强将显著有助于编写保持良好路径的代码,提高可读性并减少复杂性。