funcmain() { var err error = 1// err 的静态类型为 error // cannot use 1 (type int) as type error in assignment: // int does not implement error (missing Error method) }
编译器会在编译阶段对所有接口类型变量的赋值操作进行类型检查
如果右值的类型没有实现接口方法集合中的所有方法,会编译报错
动态特性
接口类型变量在运行时存储了右值的真实类型信息(即接口类型变量的动态类型)
拥有与动态语言相近的灵活性
1 2 3
var err error err = errors.New("this is an error") fmt.Printf("%T\n", err) // *errors.errorString
funcmain() { // Man 和 Woman 都是 Duck Type,本身没啥联系,只是都实现了 Run 方法,满足 Human 接口 animals := []Human{new(Man), new(Woman)} for _, human := range animals { HumanRun(human) } // Output: // *main.Man // man run // *main.Woman // woman run }
type eface struct { _type *_type data unsafe.Pointer }
type iface struct { tab *itab data unsafe.Pointer }
data :指向当前赋值给该接口类型变量的动态类型变量的值
Runtime
Desc
eface - empty interface
用于表示没有方法的空接口类型变量,即 interface{} 类型变量
iface
用于表示拥有方法的接口类型变量
tab 和 _type 可以统一看作动态类型的信息
Go 中每种类型都会有唯一的 _type 信息,无论是内置原生类型,还是自定义类型
Go 运行时会为程序内的全部类型建立只读的共享 _type 信息表
拥有相同动态类型的同类接口变量的 _type/tab 信息是相同的
data 指向一个动态分配的内存空间(存储赋值给接口类型变量的动态类型变量的值)
未显式初始化的接口类型变量的值为 nil,即对应的 _type/tab 和 data 都为 nil
eface
_type:接口类型变量的动态类型的信息
runtime/type.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
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 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
package main
type T struct { n int s string }
funcmain() { var t = T{ n: 17, s: "hello, interface", }
var ei interface{} = t // Go 运行时使用 eface 结构表示 ei }
_type 指向动态类型 T 的类型信息,而 data 指向动态类型 T 的实例
iface
除了需要存储动态类型信息,还需要存储接口本身的信息,因此需要一个额外的结构体 itab
runtime/runtime2.go
1 2 3 4 5 6 7
type itab struct { inter *interfacetype _type *_type 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. }
inter:存储接口类型自身的信息
runtime/type.go
1 2 3 4 5
type interfacetype struct { typ _type // 类型信息 pkgpath name // 包路径名 mhdr []imethod // 由接口方法集合组成的切片 }
funcconvT2E(t *_type, elem unsafe.Pointer) (e eface) { if raceenabled { raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E)) } if msanenabled { msanread(elem, t.size) } x := mallocgc(t.size, t, true) // TODO: We allocate a zeroed object only to overwrite it with actual data. // Figure out how to avoid zeroing. Also below in convT2Eslice, convT2I, convT2Islice. typedmemmove(t, x, elem) e._type = t e.data = x return }