go里边一个让新手误解,让老炮犯错的小知识点

问题:在一个nil变量上调用函数,会发生什么?
一般会认为panic。为了不发生panic,每次调用方法前,都要判断变量是否为nil。这么做虽然没错,但终究使代码看起来不那么优雅。
那么一定会panic吗?
让我们来做个实验

type PlanConfig struct {
    No int64
}
func (p *PlanConfig) IsEmpty() bool {
    if p == nil {
        fmt.Println("hello")
        return false
    }
    return reflect.ValueOf(*p).IsZero()
}
var p *PlanConfig
fmt.Println(p)
fmt.Println(p.IsEmpty())

该代码会输出

hello
false
可以看到,新声明的指针类型变量p,是nil,但是并不妨碍他调用IsEmpty()方法。

如果强制赋nil呢,还会执行吗?

type PlanConfig struct {
    No int64
}
func (p *PlanConfig) IsEmpty() bool {
    if p == nil {
        fmt.Println("hello")
        return false
    }
    return reflect.ValueOf(*p).IsZero()
}
var p *PlanConfig
fmt.Println(p)
p = nil
fmt.Println(p.IsEmpty())

依然输出

hello
false
是不是很顽强。

再模拟一下我们实际代码中的用法,比如从一个函数获取某个变量,但是返回值nil,然后用这个变量调用方法

type PlanConfig struct {
    No int64
}
func (p *PlanConfig) IsEmpty() bool {
    if p == nil {
        fmt.Println("hello")
        return false
    }
    return reflect.ValueOf(*p).IsZero()
}

func getPlan() *PlanConfig {
    return nil
}
p := getPlan()
fmt.Println(p.IsEmpty())

输出
hello
false

这样我们只需要在方法的入口处判断一次即可,而不用每次调用方法前,都判断变量是否为nil了。
我们再看看,程序会崩的情况。

type PlanConfig struct {
    No int64
}
func (p *PlanConfig) IsEmpty() bool {
    if p == nil {
        fmt.Println("hello")
        return false
    }
    return reflect.ValueOf(*p).IsZero()
}

p := nil
fmt.Println(p.IsEmpty())

ide直接会飘红。执行代码也会抛出错误
use of untyped nil

再看这种

type PlanConfig struct {
    No int64
}
func (p *PlanConfig) IsEmpty() bool {
    
    return reflect.ValueOf(*p).IsZero()
}

func getPlan() *PlanConfig {
    return nil
}
p := getPlan()
fmt.Println(p.IsEmpty())

这一次,我们在方法 IsEmpty()入口处去掉 nil判断,结果确实会发生panic。

结论:
用指针作为方法接收者的时候,只要我们在函数入口处做是否为nil的判断,那么就可以尽情去享受go带来的便利了。

你可能感兴趣的:(go)