谨慎对待Go语言中对interface的nil判断
在进行Go语言编程中,我们会看见诸如if err == nil {}
或者if err != nil {}
之类的判断,这跟go语言的错误处理哲学(计划失败而非成功 / 及早失败)有关;
大多数情况下,我们对一个err
是否是nil
进行判断是很直白的,无需考虑很多,但是如果对于interface的情况来说,处理的时候稍有点难度,多了许多要留意的地方。
看个重要的例子
package main
import (
"bytes"
"fmt"
"io"
)
//check的参数类型是io.Writer,是一个interface,所以只要某个类型的变量具有了Write方法
//也就等同于实现了Writer这个接口
//Writer的源码定义如下
//type Writer interface {
// Write(p []byte) (n int, err error)
//}
func check(w io.Writer) {
if w != nil {
fmt.Println("w不是nil")
}
fmt.Printf("w是 %+v\n",w)
}
func main() {
var b *bytes.Buffer //默认值是nil
//bytes包里面的Buffer是一个struct,它具有Write方法,也就实现了io.Writer接口
//从而可以作为参数
//func (b *Buffer) Write(p []byte) (n int, err error)
check(b)
fmt.Printf("b的值是%+v\n",b)
}
大家可以先猜一下输出结果是什么?
.
.
.
.
.
.
w不是nil
w是 <nil>
b的值是<nil>
不知道你猜对了没?但是对于新手来说,这一点确实有点困惑,w怎么一会儿是nil,一会儿又不是呢?
其实这个奇怪的现象跟go语言中interface的内部结构有关, interface
内部由一个类型T
和一个值V
构成,其中V是一个具体类型(int, struct, pointer,string…)的值,比如int类型的10
或者string类型的“wdy”
重点来了
一个interface要想为nil,只有可能是它内部的 T 和 V 都为nil.
阅读下面代码的前提【知识储备-源码】
//我们知道error是一个类型,同时在go语言的源码定义中,error也是一个interface
type error interface {
Error() string
}
//所以任意一个类型拥有了方法 func (any <any Type>) Error() string {}就是实现了error这个interface
//比如:
type some struct{}
func (s *some) Error() string {
return "some"
}
//这个代码段说明*some这个类型实现了error这个interface
package main
import(
"fmt"
)
type someError struct{} //类型定义
//*someError这个类型通过定义Error()函数实现了error这个接口
func (se *someError) Error() string {
return "wdy"
}
func check(e error) {
if e != nil {
fmt.Println("e 不是nil")
}else{
fmt.Println("e 是 nil")
}
fmt.Printf("e的值是%+v\n",e)
}
func main() {
//情况1
var e error = nil
check(e)
fmt.Println()
//情况2
var se *someError = nil
check(se)
}
同样请猜一下输出结果:
.
.
.
.
.
.
e 是 nil
e的值是<nil>
e 不是nil
e的值是wdy
记得实操一遍哦,自己在Go playground上面动手,尝试自己写一个例子。