欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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()
}

运行结果

golang sql绑定变量_记录一个golang的nil类型有意思的case

结论

我们通过反射,看出结构体空指针与普通的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()
}

运行结果

golang sql绑定变量_记录一个golang的nil类型有意思的case

结论

这次我们命名了一个接口,并且创建了一个空的接口变量,发现它的类型和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
}

运行结果

golang sql绑定变量_记录一个golang的nil类型有意思的case

结论

这就比较明显了,虽然空的结构体指针可以调用对应的函数,但是绝对不能访问其内部字段。

我们在来看一个比较有意思的事情。

  • 代码
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()
}

运行结果

golang sql绑定变量_记录一个golang的nil类型有意思的case

结论

其实也比较好理解,我们把一个空指针,强转成一个*AA类型,必然给它附上了对应的函数指针,它就跟纯nil不太一样了。

最后

其实,在实验的过程中产生了很多的疑问,我会在后续的文章给出一一解答。

  1. golang 的interface底层是什么结构?(其实有iface与eface两种,区别为是否包含函数)
  2. golang的结构体与interface又是什么联系,结构体绑定函数后就可以作为interface,golang的编译器在绑定函数的时候做了什么?
  3. golang的结构体的函数存在哪里,函数调用的时候是如何找到的?
  4. golang的结构体的函数绑定有值绑定与指针绑定,区别是什么?