在底层,interface 作为两个成员来实现:一个类型和一个值 (type, value)value 被称为接口的动态值,它是一个任意的具体值,而该 type 则为该值的类型。对于 int 值 3, 一个接口值示意性地包含 (int, 3)

接口的零值是 (nil, nil)。换句话说。当一个接口和 nil 比较时,只有该接口内部的值和类型都是 nil 时它才等于 nil。比如我们在一个接口值 i 中存储一个 *int 类型的指针 p,则接口 i 的内部类型将为 *int。无论指针 p 是否为 nili != nil 将永远返回 true

var i interface{}
var p *int = nil
i = p
println(i != nil)        // true

指针的零值是 nil。因此,可以先将一个接口值转为一个指针类型,然后再与 nil 比较,从而判断接口内部的值是否为 nil。举例:

println(i != nil)        // true
println(i.(*int) != nil) // false

下面这段代码将会一直 panic:

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 {
		panic(nil)
	}
}

因为该函数返回的是一个 error 类型的接口,但是却有一个具体类型 *MyErrorerr != nil 将永远返回 true

针对这个问题,可以在判断前先将 err 转换为具体类型*MyError,然后比较 err 的值是否为 nil:

func main() {
	if err := returnsError(); err.(*MyError) != nil {
		panic(nil)
	}
}

不过更好的办法是让函数返回一个纯正的 nil,这也是 Go 语言中标准的错误返回方式:

func returnsError() error {
	if bad() {
		return (*MyError)(err)
	}
	return nil // 直接返回一个 nil
}

最后,ultimate-go 也提供了一个类似的案例。


参考资料: