func(x interface{}) { if x == nil { fmt.Println("empty interface") return } fmt.Println("non-empty interface") }
funcmain() { var x *int = nil Foo(x) }
Examples of the above output follows
1 2
$ go run test_interface.go non-empty interface
Perhaps you may feel strange, why is non-empty inerface, then read on, you will know the answer.
interface substructure
The interface includes whether the method, represented in two struct the underlying implementation: iface and eface. eface method represents interface structure is free of, or called empty interface. For most data types Golang the structure can be abstracted _type, while there will be some other information for different types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
type eface struct { _type *_type data unsafe.Pointer }
type _type struct { size uintptr ptrdata uintptr// size of memory prefix holding all 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 alg *typeAlg // algorithm table gcdata *byte// garbage collection data str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero }
iface represents the underlying non-empty interface implementation. Compared to empty interface, non-empty to contain some method. Specific implementation method is stored in the itab.fun variable. If the interface contains multiple method, there exist only a fun how variable it? The following then elaborate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
type iface struct { tab *itab data unsafe.Pointer }
// layout of Itab known to compilers // allocated in non-garbage-collected memory // Needs to be in sync with // ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs. type itab struct { inter *interfacetype _type *_type link *itab bad int32 inhash int32// has this itab been added to hash? fun [1]uintptr// variable sized }
代码 17 行 var y interface{} = x 调用了函数 runtime.convT2E,将 int 类型的 x 转换成 empty interface。代码 19 行 var t MyInterface = s 将 MyStruct 类型转换成 non-empty interface: MyInterface。
type itab struct { inter *interfacetype _type *_type link *itab bad int32 inhash int32 // has this itab been added to hash? fun [1]uintptr // variable sized }
var ( ifaceLock mutex // lock for accessing hash hash [hashSize]*itab )
func itabhash(inter *interfacetype, typ *_type) uint32 { // compiler has provided some good hash codes for us. h := inter.typ.hash h += 17 * typ.hash // TODO(rsc): h += 23 * x.mhash ? return h % hashSize }
// The assertXXX functions may fail (either panicking or returning false, // depending on whether they are 1-result or 2-result). func assertI2I(inter *interfacetype, i iface) (r iface) { tab := i.tab if tab == nil { // explicit conversions require non-nil interface value. panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) } if tab.inter == inter { r.tab = tab r.data = i.data return } r.tab = getitab(inter, tab._type, false) r.data = i.data return } func assertI2I2(inter *interfacetype, i iface) (r iface, b bool) { tab := i.tab if tab == nil { return } if tab.inter != inter { tab = getitab(inter, tab._type, true) if tab == nil { return } } r.tab = tab r.data = i.data b = true return }
// 类似 func assertE2I(inter *interfacetype, e eface) (r iface) func assertE2I2(inter *interfacetype, e eface) (r iface, b bool)
4. Summary
In a sense, Golang the interface is also a manifestation of polymorphism. Compared to other language support multi-state properties, or to achieve slightly different, hard to say who is good and who's bad.