背景

Golang 中, 一般通过返回 error 判定函数异常. 常用的模式是:

if err != nil {
    panic(err) // 更多的是 return err
}

这样的代码片会遍布代码库时就会很烦, 所以有人就想少写两行:

checkErr(err)

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

当然, 有人反对 checkErr 的写法: Why You Should Not Use checkErr, 这里不讨论偏好, 关注的是: 这样写严谨吗?

此 nil 非彼 nil

这里我们需要一个自定义的错误(实现了error interface):

type CustomError struct {}
func (err *CustomError) Error() string {
	return "CustomError"
}

场景1: 声明一个自定义错误指针变量, 然后与 nil 比较:

https://play.golang.org/p/lzG8UcRovrX

var err *CustomError
fmt.Println(err == nil) // return true

看起来很正常!

场景2: 声明一个函数, 返回 nil:

https://play.golang.org/p/Zf_r1CPAz2W

func test() error {
	return nil
}

fmt.Println(test() == nil) // return true

似乎也没问题!

场景2+: 声明一个函数, 返回自定义错误 nil 指针:

https://play.golang.org/p/hGhXxWSZ6wM

func test() error {
	var err *CustomError
	return err
}

fmt.Println(test() == nil) // return false

嗯? 返回不是 true ?

场景3: 声明函数, 返回自定义错误指针

https://play.golang.org/p/PEKq5eYOkVC

func test() *CustomError {
	return nil
}

fmt.Println(test() == nil) // return true

又返回 true 了?

场景3+: 声明函数, 返回自定义错误指针

https://play.golang.org/p/WOatukzyYgi

func test() *CustomError {
	var err *CustomError
	return err
}

fmt.Println(test() == nil)	// return true

也是 true.

背后的原因

官方文档对此做了说明: Why is my nil error value not equal to nil?, 一句话解释是: error 是 interface, 比较时会同时比较 类型.

如何正确的判断 nil error ?

使用反射. 具体可以看 stretchr/testify 中的 isNil 实现

func isNil(object interface{}) bool {
	if object == nil {
		return true
	}

	value := reflect.ValueOf(object)
	kind := value.Kind()
	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
		return true
	}

	return false
}

回到 checkErr

这里给个不同方式的对比, 对照官方的解释理解下: https://play.golang.org/p/_o5nfQYt3nl

场景err == nil普通版本 isNilstretchr/testify 版本 isNil
imagetruetruetrue
imagetruetruetrue
imagefalsefalsetrue
imagetruefalsetrue
imagetruefalsetrue

实践建议

函数定义时, 返回的错误永远是error类型

func a() *CustomError // bad
func b() error // good

如果返回的错误是自定义类型, 需要返回空时明确return nil

// bad
var err *CustomError
return err

// good
return nil

参考资料