【go语言之interface上】

说到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,判断是否是非空接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值