说到go的interface,可以动态的赋予类型.
interface又分为空接口和非空接口。
空接口就是申明一个类型为interface的变量。
var a interface{}
然后非空接口就是带方法的,需要实现指定方法的。
type iface interface {
get()
}
接下来分析下空接口和非空的源码实现,基于go1.18.2。
空接口
文件是src/runtime/runtime2.go
type eface struct {
_type *_type
data unsafe.Pointer
}
而_type是
// Needs to be in sync with ../cmd/link/internal/ld/decodesym.go:/^func.commonsize,
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.dcommontype and
// ../reflect/type.go:/^type.rtype.
// ../internal/reflectlite/type.go:/^type.rtype.
type _type struct {
size uintptr
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32
tflag tflag
align uint8
fieldAlign uint8
kind uint8
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
// 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
}
go官方为了使用方便在reflect中定义了相同类型。注释也能看出来,两边要保持一致
// rtype is the common implementation of most values.
// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
从eface 名字也可以看出来,e就是empty的意思,也就是非空接口。
data 就是指向数据的指针。
然后因为interface是可以动态赋值,所以需要知道,赋值类型的信息。而_type就是赋值类型,也就是真正类型的信息。
_type还是所有类型元数据的"header",也就是说每个类型都会带上的一个结构体,接下来我们就来简单说一下这个类型元数据和这个_type。
类型元数据
都知道golang有很多类型,有 int,string,chan,map,struct。对于int,string这些来说,底层的结构就是_type。那么对于其他类型来说,包括指针,结构体又有自己类型。
结构体
结构体就是
type struct{}
底层源码是
type structtype struct {
typ _type
pkgPath name // 包引入路径
fields []structfield // 结构体字段
}
// 结构体字段
type structfield struct {
name name // 名称
typ *_type // 结构体成员的类型
offsetAnon uintptr // 字段的偏移量
}
// 偏移量
func (f *structfield) offset() uintptr {
return f.offsetAnon >> 1
}
然后来看一下 上面的name,对应的结构体
type name struct {
bytes *byte
}
// 通过指针偏移得到底层的byte的指针 此处忽略whySafe字符串
func (n name) data(off int, whySafe string) *byte {
return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe))
}
// 判断是否导出 这里通过判断大小写
func (n name) isExported() bool {
return (*n.bytes)&(1<<0) != 0
}
// 是否有标签 通过在反序列时候用的最多
/*
type name struct{
s string `json`
}
*/
func (n name) hasTag() bool {
return (*n.bytes)&(1<<1) != 0
}
// readVarint parses a varint as encoded by encoding/binary.
// It returns the number of encoded bytes and the encoded value.
// 主要是序列化用,返回序列化的字节数和值
func (n name) readVarint(off int) (int, int) {
v := 0
for i := 0; ; i++ {
x := *n.data(off+i, "read varint")
v += int(x&0x7f) << (7 * i)
if x&0x80 == 0 {
return i + 1, v
}
}
}
指针
通常用法就是
var a *string
看一下指针的源码,这里我们用的reflect/type.go中的源码,和上面src/runtime/runtime2.go定义是一样的,因为go为了使用方面,所以在reflect/type.go重新定义了一遍。
type ptrType struct {
rtype // 指针元数据
elem *rtype // 指向的类型的元数据
}
方法
就是我们在go中定义的func
// funcType represents a function type.
//
// A *rtype for each in and out parameter is stored in an array that
// directly follows the funcType (and possibly its uncommonType). So
// a function type with one method, one input, and one output is:
//
// struct {
// funcType
// uncommonType
// [2]*rtype // [0] is in, [1] is out
// }
type funcType struct {
rtype
inCount uint16
outCount uint16 // top bit is set if last input parameter is ...
}
// 入参
func (t *funcType) in() []*rtype {
uadd := unsafe.Sizeof(*t)
if t.tflag&tflagUncommon != 0 {
uadd += unsafe.Sizeof(uncommonType{})
}
if t.inCount == 0 {
return nil
}
return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.inCount:t.inCount]
}
// 出参
func (t *funcType) out() []*rtype {
uadd := unsafe.Sizeof(*t)
if t.tflag&tflagUncommon != 0 {
uadd += unsafe.Sizeof(uncommonType{})
}
outCount := t.outCount & (1<<15 - 1)
if outCount == 0 {
return nil
}
return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "outCount > 0"))[t.inCount : t.inCount+outCount : t.inCount+outCount]
}
根据官方的注释解释一下funcType的定义。首先rtype这个没的说就是基本定义,然后inCount和outCount就是一个方法的入参的数量和出参的数量。
然后入参和出参是通过funcType的in和out方法去获取到一个*rtype的数组,分别通过inCount和outCount获取到。
然后uncommonType是对于自定义类型为func的,这个后面说。
[2]*rtype 这个应该就是入参和出参的指针,指向in和out对应的数组的第一个地址。
切片
切片就是在go中定义的
var a []int
看一下在底层的实现
// sliceType represents a slice type.
type sliceType struct {
rtype
elem *rtype // slice element type
}
看注释可以看出和interface类似,rtype就是slice的原数据,elem就是切片中每个元素的元数据。
数组
数组就是go中定义的
var a [5]int
底层的实现是
// arrayType represents a fixed array type.
type arrayType struct {
rtype // 数组元数据
elem *rtype // 数组每个变量的元数据
slice *rtype // 切片类型 因为数组和slice可以公用因此也保留的切片的类型指针
len uintptr // 长度
}
管道
管道就是go中定义的
var a chan int
底层的实现是
// chanType represents a channel type.
type chanType struct {
rtype
elem *rtype // channel 中元素的类型
dir uintptr // channel 的方法 见下面的ChanDir
}
// ChanDir represents a channel type's direction.
type ChanDir int
const (
RecvDir ChanDir = 1 << iota // <-chan
SendDir // chan<-
BothDir = RecvDir | SendDir // chan
)
map
map就是go中定义的
var a map[int]int
在底层中的实现是
// mapType represents a map type.
type mapType struct {
rtype
key *rtype // map key type
elem *rtype // map element (value) type
bucket *rtype // internal bucket structure
// function for hashing keys (ptr to key, seed) -> hash
hasher func(unsafe.Pointer, uintptr) uintptr
keysize uint8 // size of key slot
valuesize uint8 // size of value slot
bucketsize uint16 // size of bucket
flags uint32
}
这个可以说一下bucket。bucket是map底层用的,后面说到map源码会说到。
hasher 就是成对key进行随机数生成然后根据高低位分配。
flags 标志位,如果 key数量大于128设置第一位为1(flags |= 1).如果value的数量大于128第二位设置为2(flags |= 2).
interface
interface就是通常定义的
var a interface{}
底层的实现是
// imethod represents a method on an interface type
type imethod struct {
name nameOff // name of method
typ typeOff // .(*FuncType) underneath
}
// interfaceType represents an interface type.
type interfaceType struct {
rtype
pkgPath name // import path
methods []imethod // sorted by hash
}
pkgPath 就是引入的包路径。
methods 就是interface的方法。对于非空的interface而言,会定义需要实现的方法。如下:
type Writer interface {
Write(p []byte) (n int, err error)
}
这个和eface和iface的区别是,个人理解 eface和iface是根据定义的类型更为具体的信息,而interfaceType是宽泛的信息,见
在这一段methodReceiver的源码中,先判断类型是不是interface。然后指针转成成interfaceType,然后根据Value的ptr也就是指针,将指针的值转换成nonEmptyInterface,也就是iface,判断是否是非空接口