先看一段代码
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。欲了解更多信息,请参阅反射法则。
网友评论