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()方法。
【go里边一个让新手误解,让老炮犯错的小知识点】如果强制赋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带来的便利了。

    推荐阅读