golang sql绑定变量_记录一个golang的nil类型有意思的case
程序员文章站
2024-03-12 23:38:51
...
背景
定位线上服务panic的问题,捕获的panic栈日志显示某一行出现nil指针操作,很常见的一个panic问题,去定位到项目中该行代码,发现有好两处对指针的操作,类似如下的操作
type Item struct {
A int
}
func (i *Item)Add(number int) int{
return number + 1
}
func main (){
.....
var item *Item
if item.Add(number) > 10 && item.A > 20 {
....
}
}
本以为很简单,item为空指针,不能对其进行操作。但是,突然发现一个有意思的现象,item.Add函数竟然执行成功了,导致panic的原因是后面的item.A。如果你看到这里,觉得这不是大家都知道的事情吗,那就不用往下看了。
原因探索
其实仔细思考下,原因也很简单,item虽然值为nil,但是它的类型是Item,Item类型绑定了Add函数,而且Add并没有方位对象的任何变量,只是单纯的对参数做了加法返回,而导致panic的item.A 是因为访问了对象,接下来我们通过实际操作,一一验证我们的想法。
验证
- 代码
package main
import (
"fmt"
"reflect"
)
type AA struct {
x int
}
func (this *AA) a() {
fmt.Println("你好")
}
func main() {
var aa *AA
fmt.Println(reflect.TypeOf(aa),reflect.TypeOf(nil))
fmt.Println(reflect.ValueOf(aa),reflect.ValueOf(nil))
aa.a()
}
运行结果
结论
我们通过反射,看出结构体空指针与普通的nil还是不同的,结构体指针的类型是可以获取到的,相应的它对应的函数应该可以执行。
- 代码
package main
import (
"fmt"
"reflect"
)
type A interface {
a()
}
type AA struct {
x int
}
func (this *AA) a() {
fmt.Println("你好")
}
func main() {
var aa A
fmt.Println(reflect.TypeOf(aa),reflect.TypeOf(nil))
fmt.Println(reflect.ValueOf(aa),reflect.ValueOf(nil))
aa.a()
}
运行结果
结论
这次我们命名了一个接口,并且创建了一个空的接口变量,发现它的类型和value竟然与真正的nil完全相同,至于为什么,可能需要单独写一篇有关interface的结构的文章来解释。但是在这里我们大致可以理解,空的接口变量,与实现该接口的结构体的空指针是完全不同的。结构体空指针应该是在某个地方存着该结构体的函数变量,而空的接口变量对应的函数指针是nil。
- 代码
package main
import (
"fmt"
)
type A interface {
a()
}
type AA struct {
x int
}
func (this *AA) a() {
fmt.Println("你好")
}
func main() {
var A *AA
A.x = 1
}
运行结果
结论
这就比较明显了,虽然空的结构体指针可以调用对应的函数,但是绝对不能访问其内部字段。
我们在来看一个比较有意思的事情。
- 代码
package main
import (
"fmt"
)
type A interface {
a()
}
type AA struct {
x int
}
func (this *AA) a() {
fmt.Println("你好")
}
func main() {
a := (*AA)(nil)
a.a()
}
运行结果
结论
其实也比较好理解,我们把一个空指针,强转成一个*AA类型,必然给它附上了对应的函数指针,它就跟纯nil不太一样了。
最后
其实,在实验的过程中产生了很多的疑问,我会在后续的文章给出一一解答。
- golang 的interface底层是什么结构?(其实有iface与eface两种,区别为是否包含函数)
- golang的结构体与interface又是什么联系,结构体绑定函数后就可以作为interface,golang的编译器在绑定函数的时候做了什么?
- golang的结构体的函数存在哪里,函数调用的时候是如何找到的?
- golang的结构体的函数绑定有值绑定与指针绑定,区别是什么?