1. 程式人生 > >Go語言interface底層實現

Go語言interface底層實現

Go的interface原始碼在Golang原始碼的runtime目錄中。
Go在不同版本之間的interface結構可能會有所不同,但是,整體的結構是不會改變的,此文章用的Go版本是1.11。

Go的interface是由兩種型別來實現的:ifaceeface
其中,iface表示的是包含方法的interface,例如:

type Person interface {
	Print()
}

eface代表的是不包含方法的interface,即

type Person interface {}

或者

var person interface{} = xxxx實體

eface

eface

的具體結構是:
在這裡插入圖片描述
一共有兩個屬性構成,一個是型別資訊_type,一個是資料資訊。
其中,_type可以認為是Go語言中所有型別的公共描述,Go語言中幾乎所有的資料結構都可以抽象成_type,是所有型別的表現,可以說是萬能型別,
data是指向具體資料的指標。

type的具體程式碼為:

type _type struct {
	size       uintptr 
	ptrdata    uintptr // size of memory prefix holding all pointers
	hash       uint32
	tflag      tflag
	align      uint8
fieldalign uint8 kind uint8 alg *typeAlg // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte str nameOff ptrToThis typeOff }

eface的整體結構是:
在這裡插入圖片描述

對於沒有方法的interface賦值後的內部結構是怎樣的呢?
可以先看段程式碼:

import (
	"fmt"
	"strconv"
)

type Binary uint64

func main() {
	b := Binary(200)
	any := (interface{})(b)
	fmt.Println(any)
}

輸出200,賦值後的結構圖是這樣的:
在這裡插入圖片描述
對於將不同型別轉化成type萬能結構的方法,是執行時的convT2E方法,在runtime包中。
以上,是對於沒有方法的介面說明。
對於包含方法的函式,用到的是另外的一種結構,叫iface

iface

所有包含方法的介面,都會使用iface結構。包含方法的介面就是一下這種最常見,最普通的介面:

type Person interface {
	Print()
}

iface的原始碼是:

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

iface的具體結構是:

在這裡插入圖片描述

itabiface不同於eface比較關鍵的資料結構。其可包含兩部分:一部分是確定唯一的包含方法的interface的具體結構型別,一部分是指向具體方法集的指標。
具體結構為:
在這裡插入圖片描述
屬性 itab的原始碼是:

type itab struct {
	inter *interfacetype //此屬性用於定位到具體interface
	_type *_type //此屬性用於定位到具體interface
	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.
}

屬性interfacetype類似於_type,其作用就是interface的公共描述,類似的還有maptypearraytypechantype…其都是各個結構的公共描述,可以理解為一種外在的表現資訊。interfacetype原始碼如下:

type interfacetype struct {
	typ     _type
	pkgpath name
	mhdr    []imethod
}
type imethod struct {
	name nameOff
	ityp typeOff
}

iface的整體結構為:

在這裡插入圖片描述

對於含有方法的interface賦值後的內部結構是怎樣的呢?
一下程式碼執行後

package main

import (
	"fmt"
	"strconv"
)

type Binary uint64
func (i Binary) String() string {
	return strconv.FormatUint(i.Get(), 10)
}

func (i Binary) Get() uint64 {
	return uint64(i)
}

func main() {
	b := Binary(200)
	any := fmt.Stringer(b)
	fmt.Println(any)
}

首先,要知道程式碼執行結果為:200。
其次,瞭解到fmt.Stringer是一個包含String方法的介面。

type Stringer interface {
	String() string
}

最後,賦值後接口Stringer的內部結構為:
在這裡插入圖片描述

對於將不同型別轉化成itable中type(Binary)的方法,是執行時的convT2I方法,在runtime包中。