为什么Go中的nil错误值不等于nil?|Go主题月

1,240 阅读2分钟

在后台,接口被实现为两个元素,一个是类型T,一个是值V,V是一个具体的值,如int、struct或指针,绝不是接口本身,其类型为T。例如,如果我们将int值3存储在一个接口,那么所产生的接口值的示意为:(T=int,V=3)。V值也被称为接口的动态值,因为在程序执行过程中,一个给定的接口变量可能会持有不同的值V(以及相应的类型T)。

只有当V和T都未设置时,接口值才为nil, (T=nil, V未设置),特别地,一个nil接口将始终持有一个nil类型。如果我们在一个接口值里面存储一个类型为*int的nil指针,无论指针的值是多少,内部类型都将是*int: (T=*int, V=nil)因此,即使里面的指针值V是nil,这样的接口值也将是非nil。

这种情况可能会让人感到困惑,当nil值存储在接口值中,比如返回错误时,就会出现这种情况:

func returnsError() error {
	var p *MyError = nil
	if bad() {
		p = ErrBad
	}
	return p // Will always return a non-nil error.
}

如果一切顺利,函数返回一个nil的p,所以返回值是一个错误接口值(T=*MyError, V=nil)。这意味着,如果调用者将返回的错误与nil进行比较,即使没有什么不好的事情发生,它也总是看起来像有错误。为了给调用者返回一个正确的nil错误,函数必须返回一个显式的nil:

func returnsError() error {
	if bad() {
		return ErrBad
	}
	return nil
}

对于返回错误的函数来说,最好总是在其签名中使用错误类型(正如我们上面所做的那样),而不是使用像*MyError这样的具体类型,这有助于确保正确创建错误。举个例子,os.Open 返回一个错误,即使它不是 nil,也总是使用具体类型 *os.PathError

只要使用了接口,就会出现与这里描述的类似情况。只要记住,如果在接口中存储了任何具体的值,接口就不会是nil。更多信息,请参见《反射法则》。

golang外文翻译计划golang.org/doc/faq#cre…