如下代码
package note
import (
"fmt"
"testing"
)
type Animal interface {
Speak()
}
type Dog struct {
}
func (d *Dog) Speak() {
fmt.Print("dog speak")
}
func TestInterface(T *testing.T) {
var animal Animal
if animal == nil {
fmt.Println("nil animal")
}
var dog *Dog
if dog == nil {
fmt.Println("nil dog")
}
animal = dog
if animal == nil {
fmt.Println("nil animal")
}
}
最终输出
=== RUN TestInterface
nil animal
nil dog
--- PASS: TestInterface (0.00s)
PASS
可以看到当dog(nil)赋值给animal(nil)之后,animal不再为nil
原因如下
具体在runtime/runtime2.go
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/reflectdata/reflect.go:/^func.WritePluginTable.
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.
}
-
tab:指向itab结构的指针,该结构包含该类型的信息以及该类型为接口实现的方法 -
data: 指向接口实际数据的指针 -
inter *interfacetype:指向接口类型的指针。interfacetype是表示接口类型的结构体。 -
_type *_type:指向具体类型的指针。_type是表示具体类型的结构体。 -
hash uint32:具体类型的哈希值副本,用于类型转换。该哈希值是_type.hash的副本,用于在类型转换中快速比较类型。 -
_ [4]byte:填充字节,用于内存对齐。这是为了确保结构体的内存布局符合对齐要求。 -
fun [1]uintptr:变长数组,存储函数指针。数组的第一个元素fun[0]用于指示具体类型是否实现了接口。如果fun[0]等于0,表示具体类型没有实现接口。
当执行赋值操作时,创建的iface结构体的tab不为nil,inter为&dog,_type为Dog这个类型,而data则是一个为nil的结构体&dog,所以赋值后的animal不为nil