为什么nil error不等于nil

2018-01-06  本文已影响104人  绝望的祖父

先看一段代码

package main

type Error struct {
    errCode uint8
}

func (e *Error) Error() string {
    switch e.errCode {
    case 1:
        return "file not found"
    case 2:
        return "time out"
    case 3:
        return "permission denied"
    default:
        return "unknown error"
    }
}

func checkError(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {
    var e *Error
    checkError(e)
}

此时e是nil的,但是当checkError时却会panic,为什么?

在底层,接口被实现为两个元素,一个类型和一个值。该值被称为接口的动态值, 它是一个任意的具体值。而该接口的类型就是该值的类型。对于int值3,接口值示意性地包含(int,3)。

只有在接口内部的值和类型同时为nil时,一个接口的值才为nil,特别是,一个nil接口将总是包含一个nil类型。如果我们在一个interface中存储了一个int类型的空指针,那么无论这个int指针的值是什么,接口的内部类型值都将为*int。因此,这样的接口值将会是非nil的,即使该内部指针是一个nil指针。

这种情况可能会引起混淆,在一个接口值(如error返回值)中存储一个nil时会出现这种情况:

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

如果一切顺利,函数返回一个nil p,所以返回值是一个error的接口值(* MyError,nil)。这意味着,如果调用者将返回的error与nil做相等比较,即使没有发生任何错误,对比的结果也会是false。为了给调用者返回一个适当的nil error,函数必须返回一个明确的nil:

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

对于返回error的函数来说,总是在签名中使用error类型(如上所述)而不是像MyError这样的具体类型,这是个好习惯,以帮助保证正确创建error。作为一个例子,os.Open返回一个错误,即使不是nil,它总是具体的类型 *os.PathError。

无论何时使用接口,都会出现类似于这里描述的情况。请记住,如果接口中存储了具体的值,接口将不会为nil。欲了解更多信息,请参阅反射法则

参考文献:Why is my nil error value not equal to nil?

上一篇下一篇

猜你喜欢

热点阅读