Golang 中有一个表面上看起来很奇怪的的问题,如下的代码中的函数会始终返回一个不等于 nilerror.

func returnsError() error {
	var p *MyError = nil
	if bad() {
		p = ErrBad
	}
	return p // Will always return a non-nil error.
}
 
func main() {
	if err = returnsError(); err != nil {
		// Will always print
		fmt.Println("ERROR")
	}
}

要解释这个问题的原因,我们需要了解 Golang 中 interface 的内部实现。对于 interface 的实例 Golang 内部分为两个实现,ifaceeface, 对应的代码在 runtime/runtime2.go 中。

type iface struct {
	tab  *itab
	data unsafe.Pointer
}
 
type eface struct {
	_type *_type
	data  unsafe.Pointer
}
  • eface 用于表示没有方法的空接口( empty interface )类型变量,即 interface{} 类型的变量。
  • iface 用于表示其余拥有方法的接口类型变量。

我们可以理解为 interface 的内部实现中包含两个字段 (T, V) ,只有当 T 和 V 同时为 nil 时 (T=nil, v=nil) ,才能被判断为与 nil 相等。

在上面的例子中 var p *MyError = nil 相当于 var p *MyError = &MyError(nil) ,p 被初始化为了 (T=*MyError, V=nil) 的内部表示,所以始终不等于 nil 。所以对于声明为 AnyNonEmptyInterfaceType 的变量来说,始终会被初始化为非 nil 。而对声明为 interface{} 的变量来说,p interface{} = nil 的情况下,可以被判断为 nil。

所以 Golang 建议在所有有返回 error 的函数声明中都使用 error 类型来作 为函数的签名,如下:

func returnError() error {} // good
func returnError1() *MyError {} // bad

Golang 官方 FAQ 中对于该问题的解释 Golang FAQ