在Golang开发过程中,我们经常会遇到需要判断指针是否为nil的场景,比如函数接收指针参数时需要校验参数有效性,或者处理接口返回值的时候需要确认是否返回了有效指针。很多新手开发者会觉得判断nil很简单,直接用==比较就行,但实际开发中经常会遇到不符合预期的情况,尤其是涉及interface类型的时候。

基础指针类型的nil判断
对于Golang中的基础指针类型,比如*int、*string、自定义结构体的指针类型,判断是否为nil直接使用==运算符和nil比较就可以了,这是最简单也最不容易出错的情况。
下面通过一个简单的示例来演示基础指针的nil判断:
package main
import "fmt"
// 定义自定义结构体
type User struct {
Name string
Age int
}
func main() {
// 声明int类型指针,未赋值,默认是nil
var intPtr *int
fmt.Println("intPtr是否为nil:", intPtr == nil) // 输出 true
// 声明User结构体指针,未赋值,默认是nil
var userPtr *User
fmt.Println("userPtr是否为nil:", userPtr == nil) // 输出 true
// 给intPtr赋值
num := 10
intPtr = &num
fmt.Println("赋值后intPtr是否为nil:", intPtr == nil) // 输出 false
}interface类型包含指针时的nil判断误区
很多开发者会遇到这样的情况:明明一个interface变量存储的指针是nil,但是用==nil判断的时候却返回false,这是因为Golang中interface类型的nil判断不仅要看存储的值是否为nil,还要看interface的动态类型是否为nil。
interface在Golang内部是由两部分组成的:类型和值。只有当这两部分都为nil的时候,整个interface变量才是nil。如果interface的动态类型不为nil,哪怕存储的值是nil指针,整个interface变量也不等于nil。
下面通过示例演示这个误区:
package main
import "fmt"
type Animal interface {
Speak() string
}
type Dog struct{}
func (d *Dog) Speak() string {
return "汪汪"
}
// 返回Animal接口类型,实际返回的是*Dog类型的nil指针
func getAnimal() Animal {
var d *Dog
return d
}
func main() {
animal := getAnimal()
// 这里会输出false,因为animal的动态类型是*Dog,不是nil
fmt.Println("animal是否为nil:", animal == nil)
// 判断animal存储的具体值是否为nil
if v, ok := animal.(*Dog); ok {
fmt.Println("animal存储的*Dog指针是否为nil:", v == nil) // 输出 true
}
}正确的nil判断方式
针对不同的场景,我们需要采用不同的nil判断方式,避免踩坑:
- 如果是基础指针类型,直接用
指针变量 == nil判断即可。 - 如果是interface类型,需要先判断interface是否为nil,再判断内部存储的具体值是否为nil,尤其是当interface可能存储指针类型的时候。
- 如果需要判断interface是否为空,同时兼容内部存储nil指针的情况,可以先做类型断言,再判断断言后的值是否为nil。
下面是一个通用的判断interface中是否包含有效指针的示例:
package main
import "fmt"
type Animal interface {
Speak() string
}
type Cat struct{}
func (c *Cat) Speak() string {
return "喵喵"
}
func checkAnimalValid(animal Animal) bool {
// 先判断interface本身是否为nil
if animal == nil {
return false
}
// 尝试断言为*Cat类型,判断具体指针是否为nil
if catPtr, ok := animal.(*Cat); ok {
return catPtr != nil
}
// 其他类型默认认为有效
return true
}
func main() {
var cat *Cat
animal := Animal(cat)
fmt.Println("cat是否有效:", checkAnimalValid(animal)) // 输出 false
cat = &Cat{}
animal = Animal(cat)
fmt.Println("cat是否有效:", checkAnimalValid(animal)) // 输出 true
}常见注意事项
在实际开发中判断指针是否为nil的时候,还有几个需要注意的点:
不要对值为nil的指针调用方法,除非该方法是值接收者实现的方法,否则会触发panic。
当函数返回interface类型的时候,尽量不要返回具体类型的nil指针,最好直接返回nil,避免调用方判断出错。
如果不确定某个变量是不是指针类型,也可以先用反射来判断,不过反射的性能会比直接判断差一些,非必要场景不建议使用:
package main
import (
"fmt"
"reflect"
)
func isNilPtr(i interface{}) bool {
if i == nil {
return true
}
v := reflect.ValueOf(i)
// 判断是否为指针类型,且值为nil
return v.Kind() == reflect.Ptr && v.IsNil()
}
func main() {
var ptr *int
fmt.Println("ptr是否为nil指针:", isNilPtr(ptr)) // 输出 true
num := 10
ptr = &num
fmt.Println("ptr是否为nil指针:", isNilPtr(ptr)) // 输出 false
}