interface - 图1

空接口eface

空接口eface结构,由两个属性构成,一个是类型信息_type,一个是数据信息。其数据结构声明如下:

  1. type eface struct { //空接口
  2. _type *_type //类型信息
  3. data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
  4. }

_type属性:是GO语言中所有类型的公共描述,Go语言几乎所有的数据结构都可以抽象成 _type,是所有类型的公共描述,type负责决定data应该如何解释和操作,type的结构代码如下:

  1. type _type struct {
  2. size uintptr //类型大小
  3. ptrdata uintptr //前缀持有所有指针的内存大小
  4. hash uint32 //数据hash值
  5. tflag tflag
  6. align uint8 //对齐
  7. fieldalign uint8 //嵌入结构体时的对齐
  8. kind uint8 //kind 有些枚举值kind等于0是无效的
  9. alg *typeAlg //函数指针数组,类型实现的所有方法
  10. gcdata *byte
  11. str nameOff
  12. ptrToThis typeOff
  13. }

data属性: 表示指向具体的实例数据的指针,他是一个unsafe.Pointer类型,相当于一个C的万能指针void*。
interface - 图2

非空接口iface

iface 表示 non-empty interface 的数据结构,非空接口初始化的过程就是初始化一个iface类型的结构,其中data的作用同eface的相同,这里不再多加描述。

  1. type iface struct {
  2. tab *itab
  3. data unsafe.Pointer
  4. }

iface结构中最重要的是itab结构(结构如下),每一个 itab 都占 32 字节的空间。itab可以理解为pair 。itab里面包含了interface的一些关键信息,比如method的具体实现。

  1. type itab struct {
  2. inter *interfacetype // 接口自身的元信息
  3. _type *_type // 具体类型的元信息
  4. link *itab
  5. bad int32
  6. hash int32 // _type里也有一个同样的hash,此处多放一个是为了方便运行接口断言
  7. fun [1]uintptr // 函数指针,指向具体类型所实现的方法
  8. }

其中值得注意的字段,个人理解如下:

  1. interface type包含了一些关于interface本身的信息,比如package path,包含的method。这里的interfacetype是定义interface的一种抽象表示。
  2. type表示具体化的类型,与eface的 type类型相同。
  3. hash字段其实是对_type.hash的拷贝,它会在interface的实例化时,用于快速判断目标类型和接口中的类型是否一致。另,Go的interface的Duck-typing机制也是依赖这个字段来实现。
  4. fun字段其实是一个动态大小的数组,虽然声明时是固定大小为1,但在使用时会直接通过fun指针获取其中的数据,并且不会检查数组的边界,所以该数组中保存的元素数量是不确定的。


interface - 图3

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type People interface {
  6. Show()
  7. }
  8. type Student struct{}
  9. func (stu *Student) Show() {
  10. }
  11. func live() People {
  12. var stu *Student
  13. return stu
  14. }
  15. func main() {
  16. if live() == nil {
  17. fmt.Println("AAAAAAA")
  18. } else {
  19. fmt.Println("BBBBBBB")
  20. }
  21. }
  22. 输出:
  23. BBBBBBB

stu是一个指向nil的空指针,但是最后return stu 会触发匿名变量 People = stu值拷贝动作,所以最后live()放回给上层的是一个People insterface{}类型,也就是一个iface struct{}类型。 stu为nil,只是iface中的data 为nil而已。 但是iface struct{}本身并不为nil.