1. 程式人生 > 其它 >golang sql繫結變數_記錄一個golang的nil型別有意思的case

golang sql繫結變數_記錄一個golang的nil型別有意思的case

技術標籤:golang sql繫結變數

背景

定位線上服務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()
}

執行結果

7da586a81b9254a5c90dd633885411b1.png

結論

我們通過反射,看出結構體空指標與普通的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()
}

執行結果

5dd1fc589ef9629f1113d8a4b27c99d4.png

結論

這次我們命名了一個介面,並且建立了一個空的介面變數,發現它的型別和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
}

執行結果

de70896a0aec2db3498dcbf802905981.png

結論

這就比較明顯了,雖然空的結構體指標可以呼叫對應的函式,但是絕對不能訪問其內部欄位。

我們在來看一個比較有意思的事情。

  • 程式碼
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()
}

執行結果

415c527ac1c6b9b08709d55364b8c751.png

結論

其實也比較好理解,我們把一個空指標,強轉成一個*AA型別,必然給它附上了對應的函式指標,它就跟純nil不太一樣了。

最後

其實,在實驗的過程中產生了很多的疑問,我會在後續的文章給出一一解答。

  1. golang 的interface底層是什麼結構?(其實有iface與eface兩種,區別為是否包含函式)
  2. golang的結構體與interface又是什麼聯絡,結構體繫結函式後就可以作為interface,golang的編譯器在繫結函式的時候做了什麼?
  3. golang的結構體的函式存在哪裡,函式呼叫的時候是如何找到的?
  4. golang的結構體的函式繫結有值繫結與指標繫結,區別是什麼?