Go语言如何检测结构体指针是否为nil?接口赋值nil为何判断不相等?
== nil判断为nil? 这问题其实戳中了Go语言接口的一个容易踩的坑,咱们先搞清楚接口值的底层结构就明白了:
Go里的接口值其实包含两个部分:一个是类型(Type),一个是值(Value)。只有当这两部分同时为nil的时候,整个接口值才会被认为是nil。
看你的第一个代码例子:
var n localnet.Net n = localplugin.NewPluginNet() // 返回nil的*localnet.DomainSocket
当你把一个nil的*localnet.DomainSocket指针赋值给localnet.Net接口变量n时,接口的类型部分已经被设置成了*localnet.DomainSocket,而值部分确实是nil。这时候你用n == nil判断,Go会检查整个接口值的Type和Value是否都为nil——显然Type已经有值了,所以判断结果是false,哪怕打印n会显示<nil>(因为打印的时候只会输出Value部分)。
从你输出的reflect.TypeOf(n)结果也能看出来:*localnet.DomainSocket,这就是接口里存的类型,而reflect.TypeOf(nil)是<nil>,两者不一样,所以n == nil不成立。
再看第二个代码例子:
var n1 *localnet.DomainSocket n1 = nil
这里n1是一个明确的指针类型变量,它不存在接口的Type+Value结构,直接存的就是指针值。当指针值为nil时,n1 == nil自然会返回true,这就是判断正确的原因。
那怎么正确判断这种接口变量的底层值是否为nil?
给你两种常用方法:
类型断言转换后判断(推荐,更安全):
把接口转换为对应的指针类型,先确认类型匹配,再判断指针是否为nil:if ds, ok := n.(*localnet.DomainSocket); ok && ds == nil { fmt.Println("n的底层指针是nil") }用反射判断(谨慎使用,要确保接口里存的是可nil的类型,比如指针、通道、函数等,否则会panic):
if reflect.ValueOf(n).IsNil() { fmt.Println("n的底层值是nil") }
总结一下
别被接口打印出来的<nil>迷惑了,只要接口的Type部分不是nil,哪怕Value是nil,整个接口值也不是nil。要判断底层的指针是否为nil,要么用类型断言转成具体指针后判断,要么用反射检查Value是否为nil。
内容的提问来源于stack exchange,提问作者river

