Go驗(yàn)證接口和nil的比較

預(yù)備知識(shí):

  1. 任何空接口類型的變量,即interface{},其內(nèi)存布局均如下:
type eface struct {
    _type *_type                // 指向動(dòng)態(tài)類型
    data  unsafe.Pointer    // 指向動(dòng)態(tài)值
}
  1. 判斷interface{}變量是否為nil,那么就是在判斷該空接口變量的動(dòng)態(tài)類型和動(dòng)態(tài)值是否均為空(因?yàn)閮蓚€(gè)字段均為指針類型,故其零值均為0,后面你會(huì)看到代碼執(zhí)行的效果)

下面可以巧妙地運(yùn)用interface{}的內(nèi)存布局,來(lái)驗(yàn)證go關(guān)于接口類型比較的結(jié)論。

type eface struct {
    _type uintptr
    data  uintptr
}

func main() {
    var a interface{}
    var b interface{} = (*int)(nil)

    n := 1024
    var c interface{} = &n

    ia := *(*eface)(unsafe.Pointer(&a))
    ib := *(*eface)(unsafe.Pointer(&b))
    ic := *(*eface)(unsafe.Pointer(&c))

    fmt.Printf("%+v %+v %+v\n", ia, ib, ic)
    fmt.Printf("%t %t %t\n", a == nil, b == nil, c == nil)
    fmt.Println(*(*int)(unsafe.Pointer(ic.data)))

    // output: 只有_type==0 && data == 0時(shí),該interface{}變量才會(huì)為nil
    // {_type:0 data:0} {_type:17365472 data:0} {_type:17365472 data:824634805912}
    // true false false
    // 1024
}

想必你已經(jīng)看懂了。這就是我前面提到的,通過(guò)巧妙地查看接口的內(nèi)存布局,來(lái)協(xié)助我們驗(yàn)證go的一些特性。

FAQ:

  • 為什么不直接使用go內(nèi)置的eface,而是自定義一個(gè)類型?
    因?yàn)閞untime.eface不支持導(dǎo)出,只能自定義。而go里面只要內(nèi)存布局是一致的,得到的字段數(shù)據(jù)也就是和運(yùn)行時(shí)是一致的。

用上面的思路來(lái)驗(yàn)證一下非空接口:
非空接口的運(yùn)行時(shí)表示為runtime.iface,結(jié)構(gòu)如下:

type iface struct {
    tab  *itab
    data unsafe.Pointer
}

type itab struct {
    inter *interfacetype
    _type *_type
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

直接上代碼:

type iface struct {
    tab  *itab
    data uintptr
}

type itab struct {
    inter *uintptr
    _type *uintptr
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

func main() {
    var a io.Reader
    var b io.Reader = (*os.File)(nil)

    var c io.Reader = bytes.NewReader(nil)

    ia := *(*iface)(unsafe.Pointer(&a))
    ib := *(*iface)(unsafe.Pointer(&b))
    ic := *(*iface)(unsafe.Pointer(&c))
    fmt.Printf("%A %A %A\n", ia, ib, ic)  // %A是無(wú)效的格式化符號(hào),這里只是正好能打印struct內(nèi)部指針數(shù)據(jù)而已
    fmt.Printf("%t %t %t\n", a == nil, b == nil, c == nil)

    // output:
    // {%!A(*main.itab=<nil>) %!A(uintptr=0)} {%!A(*main.itab=&{0x10965c0 0x109fd00 871609668 [0 0 0 0] [16815968]}) %!A(uintptr=0)} {%!V(*main.itab=&{0x10965c0 0x109d020 2769700948 [0 0 0 0] [16815968]}) %!V(uintptr=824634359856)}
    // true false false
}

結(jié)果也是一樣的:tab和data都為nil的情況下,該接口變量才為nil。
細(xì)心的朋友可能發(fā)現(xiàn)了,b和c的inter數(shù)據(jù)一樣,但是_type值不一樣,這里說(shuō)明一下:inter代表接口類型,_type代表動(dòng)態(tài)類型。那么接口類型均為io.Reader,而動(dòng)態(tài)類型一個(gè)為*os.File,一個(gè)為bytes.Reader,當(dāng)然不同了。

參考列表:

  • 《Go程序員面試筆試寶典》
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 能力模式 選擇題 【初級(jí)】下面屬于關(guān)鍵字的是()A. funcB. defC. structD. class 參考...
    靈魂深靈閱讀 5,386評(píng)論 2 5
  • 轉(zhuǎn)載: 一文吃透 Go 語(yǔ)言解密之接口 interface[https://mp.weixin.qq.com/s/...
    Vicky丶云閱讀 765評(píng)論 0 0
  • 接口(interface)是一種抽象的類型,是對(duì)其他類型行為的概括和抽象。從語(yǔ)法角度來(lái)看,接口是一組方法簽名定義的...
    JunChow520閱讀 489評(píng)論 0 1
  • 開發(fā)環(huán)境 GOROOT: go的安裝目錄, go原生的工具在該目錄下GOPATH: 通常存放自己開發(fā)的代碼或第三方...
    Never_Yg閱讀 426評(píng)論 0 0
  • 接口 接口類型是對(duì)其他類型行為的概括與抽象。通過(guò)使用接口,我們可以寫出更加靈活和通用的函數(shù),這些函數(shù)不用綁定在一個(gè)...
    WongBynn閱讀 759評(píng)論 0 0

友情鏈接更多精彩內(nèi)容