接口的动态类型和动态值
从源码里可以看到:iface包含两个字段:tab 是接口表指针,指向类型信息;data 是数据指针,则指向具体的数据。它们分别被称为动态类型动态值。而接口值包括动态类型动态值
【引申1】接口类型和 nil 作比较
接口值的零值是指动态类型动态值都为 nil。当仅且当这两部分的值都为 nil 的情况下,这个接口值就才会被认为 接口值 == nil
来看个例子:
1
package main
2
3
import "fmt"
4
5
type Coder interface {
6
code()
7
}
8
9
type Gopher struct {
10
name string
11
}
12
13
func (g Gopher) code() {
14
fmt.Printf("%s is coding\n", g.name)
15
}
16
17
func main() {
18
var c Coder
19
fmt.Println(c == nil)
20
fmt.Printf("c: %T, %v\n", c, c)
21
22
var g *Gopher
23
fmt.Println(g == nil)
24
25
c = g
26
fmt.Println(c == nil)
27
fmt.Printf("c: %T, %v\n", c, c)
28
}
Copied!
输出:
1
true
2
c: <nil>, <nil>
3
true
4
false
5
c: *main.Gopher, <nil>
Copied!
一开始,c 的 动态类型和动态值都为 nilg 也为 nil,当把 g 赋值给 c 后,c 的动态类型变成了 *main.Gopher,仅管 c 的动态值仍为 nil,但是当 cnil 作比较的时候,结果就是 false 了。
【引申2】 来看一个例子,看一下它的输出:
1
package main
2
3
import "fmt"
4
5
type MyError struct {}
6
7
func (i MyError) Error() string {
8
return "MyError"
9
}
10
11
func main() {
12
err := Process()
13
fmt.Println(err)
14
15
fmt.Println(err == nil)
16
}
17
18
func Process() error {
19
var err *MyError = nil
20
return err
21
}
Copied!
函数运行结果:
1
<nil>
2
false
Copied!
这里先定义了一个 MyError 结构体,实现了 Error 函数,也就实现了 error 接口。Process 函数返回了一个 error 接口,这块隐含了类型转换。所以,虽然它的值是 nil,其实它的类型是 *MyError,最后和 nil 比较的时候,结果为 false
【引申3】如何打印出接口的动态类型和值?
直接看代码:
1
package main
2
3
import (
4
"unsafe"
5
"fmt"
6
)
7
8
type iface struct {
9
itab, data uintptr
10
}
11
12
func main() {
13
var a interface{} = nil
14
15
var b interface{} = (*int)(nil)
16
17
x := 5
18
var c interface{} = (*int)(&x)
19
20
ia := *(*iface)(unsafe.Pointer(&a))
21
ib := *(*iface)(unsafe.Pointer(&b))
22
ic := *(*iface)(unsafe.Pointer(&c))
23
24
fmt.Println(ia, ib, ic)
25
26
fmt.Println(*(*int)(unsafe.Pointer(ic.data)))
27
}
Copied!
代码里直接定义了一个 iface 结构体,用两个指针来描述 itabdata,之后将 a, b, c 在内存中的内容强制解释成我们自定义的 iface。最后就可以打印出动态类型和动态值的地址。
运行结果如下:
1
{0 0} {17426912 0} {17426912 842350714568}
2
5
Copied!
a 的动态类型和动态值的地址均为 0,也就是 nil;b 的动态类型和 c 的动态类型一致,都是 *int;最后,c 的动态值为 5。

参考资料

【一个包含NIL指针的接口不是NIL接口】https://i6448038.github.io/2018/07/18/golang-mistakes/
Copy link
Contents
参考资料