Golang 中有一个表面上看起来很奇怪的的问题,如下的代码中的函数会始终返回一个不等于 nil
的 error
.
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 内部分为两个实现,iface
与 eface
, 对应的代码在 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 。