結合源碼理解interface{}

首先要明確go中的interface分為兩種,無方法聲明和有方法聲明的的,對應源碼中的定義如下:

//有方法聲明
type iface struct {
    tab  *itab
    data unsafe.Pointer
}
//無方法聲明
type eface struct {
    _type *_type
    data  unsafe.Pointer
}

其中data指向實際的值信息,_type是對定義內部類型信息的數據結構,itab里定義了接口相關信息,包括接口類型、實際類型、實現的方法集等:

type itab struct {
    inter  *interfacetype
    _type  *_type
    link   *itab
    hash   uint32 // copy of _type.hash. Used for type switches.
    bad    bool   // type does not implement interface
    inhash bool   // has this itab been added to hash?
    unused [2]byte
    fun    [1]uintptr // variable sized
}

interfacetype是關于一個接口本身的信息,包括這個接口的類型、包含的方法集以及包名等。
iface則對應的是把一個struct轉為interface后的信息。
fun中存放了interfacetype需要的方法集的具體實現,因為方法內存中是順序存放的,所以fun中只需要存儲第一個方法地址的指針。

hashSize = 1009
hash      [hashSize]*itab

可能是為了性能考慮,所有的itab實際是存放在一個全局hash表中,當把一種interface類型轉為另一種interface類型時,調用的源碼是:

func convI2I(inter *interfacetype, i iface) (r iface) {
    tab := i.tab
    if tab == nil {
        return
    }
    if tab.inter == inter {
        r.tab = tab
        r.data = i.data
        return
    }
    r.tab = getitab(inter, tab._type, false)
    r.data = i.data
    return
}

可以看到當i中tab的inter和要轉的inter相同時,直接就可以轉換,否則要調用getitab獲取待轉r的itab。

func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
...
    h := itabhash(inter, typ)
    var m *itab
    var locked int
    for locked = 0; locked < 2; locked++ {
        if locked != 0 {
            lock(&ifaceLock)
        }
        for m = (*itab)(atomic.Loadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
          if m.inter == inter && m._type == typ {
...
            return m
          }
        }
    m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys))
    m.inter = inter
    m._type = typ
    additab(m, true, canfail)
    unlock(&ifaceLock)
    if m.bad {
        return nil
    }
    return m

上面的代碼,首先是用inter和typ計算一個hash值,然后看全局itab的hash表中是否含有這個hash值的itab,如果有并且這個itab的inter和typ符合條件,則證明之前已經有過類似轉換,符合類型轉換條件。如果沒有,再試圖調用additab:

func additab(m *itab, locked, canfail bool) {
    inter := m.inter
    typ := m._type
    ...
    h := itabhash(inter, typ)
    m.link = hash[h]
    m.inhash = true
    atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
}

省略號的部分是判斷m的typ是否實現了inter方法集的邏輯,如果沒有全部實現,證明類型轉換不合法m.bad會被置為true。如果轉換合法,最后會把m存入全局itab hash表中。

參考文章:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。