接口类型断言补充
空接口断言
示例:
type User struct {
name string
}
func main() {
var a interface{}
a = User{name: "hello"}
val, ok := a.(int)
if ok {
fmt.Println("is int", val)
} else {
fmt.Println("not int")
}
}
打印汇编信息:
go tool compile -S -N -l main.go > main.s
"".main STEXT size=563 args=0x0 locals=0xf8
0x0000 00000 (main.go:9) TEXT "".main(SB), ABIInternal, $248-0
0x0000 00000 (main.go:9) MOVQ TLS, CX
0x0009 00009 (main.go:9) PCDATA $0, $-2
0x0009 00009 (main.go:9) MOVQ (CX)(TLS*2), CX
0x0010 00016 (main.go:9) PCDATA $0, $-1
0x0010 00016 (main.go:9) LEAQ -120(SP), AX
0x0015 00021 (main.go:9) CMPQ AX, 16(CX)
0x0019 00025 (main.go:9) PCDATA $0, $-2
0x0019 00025 (main.go:9) JLS 553
0x001f 00031 (main.go:9) PCDATA $0, $-1
0x001f 00031 (main.go:9) SUBQ $248, SP
0x0026 00038 (main.go:9) MOVQ BP, 240(SP)
0x002e 00046 (main.go:9) LEAQ 240(SP), BP
0x0036 00054 (main.go:9) PCDATA $0, $-2
0x0036 00054 (main.go:9) PCDATA $1, $-2
0x0036 00054 (main.go:9) FUNCDATA $0, gclocals·fcf5af2016adf65a97b579a67730f1b6(SB)
0x0036 00054 (main.go:9) FUNCDATA $1, gclocals·4ade6565211d30a1bc0a23317c5a6629(SB)
0x0036 00054 (main.go:9) FUNCDATA $2, gclocals·f08c0631293fa71ce239c95c8d919476(SB)
0x0036 00054 (main.go:9) FUNCDATA $3, "".main.stkobj(SB)
0x0036 00054 (main.go:10) PCDATA $0, $0
0x0036 00054 (main.go:10) PCDATA $1, $0
0x0036 00054 (main.go:10) XORPS X0, X0
0x0039 00057 (main.go:10) MOVUPS X0, "".a+96(SP)
0x003e 00062 (main.go:11) XORPS X0, X0
0x0041 00065 (main.go:11) MOVUPS X0, ""..autotmp_3+144(SP)
0x0049 00073 (main.go:11) PCDATA $0, $1
0x0049 00073 (main.go:11) LEAQ go.string."hello"(SB), AX
0x0050 00080 (main.go:11) MOVQ AX, ""..autotmp_3+144(SP)
0x0058 00088 (main.go:11) MOVQ $5, ""..autotmp_3+152(SP)
0x0064 00100 (main.go:11) PCDATA $0, $0
0x0064 00100 (main.go:11) PCDATA $1, $1
0x0064 00100 (main.go:11) MOVQ AX, ""..autotmp_8+112(SP)
0x0069 00105 (main.go:11) MOVQ $5, ""..autotmp_8+120(SP)
0x0072 00114 (main.go:11) PCDATA $0, $1
0x0072 00114 (main.go:11) PCDATA $1, $2
0x0072 00114 (main.go:11) LEAQ type."".User(SB), AX //取User的类型信息
0x0079 00121 (main.go:11) PCDATA $0, $0
0x0079 00121 (main.go:11) MOVQ AX, "".a+96(SP) //存储User的类型信息
0x007e 00126 (main.go:11) PCDATA $0, $1
0x007e 00126 (main.go:11) PCDATA $1, $3
0x007e 00126 (main.go:11) LEAQ ""..autotmp_8+112(SP), AX
0x0083 00131 (main.go:11) PCDATA $0, $0
0x0083 00131 (main.go:11) MOVQ AX, "".a+104(SP) //存储数据的地址
上面汇编代码的作用是在栈上组装一个eface,赋值给a,因为eface是由_type和ptr组成。
"".a+96(SP)"".a+104(SP)
0x0088 00136 (main.go:12) MOVQ "".a+96(SP), AX //eface的_type
0x008d 00141 (main.go:12) PCDATA $0, $2
0x008d 00141 (main.go:12) PCDATA $1, $0
0x008d 00141 (main.go:12) MOVQ "".a+104(SP), CX
0x0092 00146 (main.go:12) PCDATA $0, $3
0x0092 00146 (main.go:12) LEAQ type.int(SB), DX //断言的类型
0x0099 00153 (main.go:12) PCDATA $0, $2
0x0099 00153 (main.go:12) CMPQ AX, DX
0x009c 00156 (main.go:12) JEQ 163
0x009e 00158 (main.go:12) JMP 544
类型断言实际上就是用eface的_type与断言的类型进行比较。
非空接口的类型断言
示例:
package main
import "fmt"
type InterUser interface {
GetName() string
}
type User struct {
name string
}
func (u User) GetName() string {
return u.name
}
func main() {
var u = User{
name: "hello",
}
i := InterUser(u) // 将u转换成InterUser类型
switch i.(type) {
case User:
fmt.Println("is User")
case InterUser:
fmt.Println("is InterUser")
}
}
打印汇编信息:
"".main STEXT size=542 args=0x0 locals=0xd0
...
0x0039 00057 (main.go:18) MOVUPS X0, "".u+72(SP)
0x003e 00062 (main.go:19) PCDATA $0, $1
0x003e 00062 (main.go:19) LEAQ go.string."hello"(SB), AX
0x0045 00069 (main.go:19) MOVQ AX, "".u+72(SP)
0x004a 00074 (main.go:19) MOVQ $5, "".u+80(SP) //分配变量u
0x0053 00083 (main.go:21) PCDATA $0, $0
0x0053 00083 (main.go:21) PCDATA $1, $1
0x0053 00083 (main.go:21) MOVQ AX, ""..autotmp_3+120(SP)
0x0058 00088 (main.go:21) MOVQ $5, ""..autotmp_3+128(SP)
0x0064 00100 (main.go:21) PCDATA $0, $1
0x0064 00100 (main.go:21) LEAQ go.itab."".User,"".InterUser(SB), AX //获取itab信息
0x006b 00107 (main.go:21) MOVQ AX, "".i+88(SP) //存储itab信息
0x0070 00112 (main.go:21) PCDATA $0, $2
0x0070 00112 (main.go:21) PCDATA $1, $0
0x0070 00112 (main.go:21) LEAQ ""..autotmp_3+120(SP), CX
0x0075 00117 (main.go:21) MOVQ CX, "".i+96(SP) //将变量u的地址赋值给i的ptr
0x007a 00122 (main.go:23) PCDATA $1, $2
0x007a 00122 (main.go:23) MOVQ AX, ""..autotmp_4+104(SP)//将变量的itab赋给autotmp_4+104(SP)
0x007f 00127 (main.go:23) PCDATA $0, $1
0x007f 00127 (main.go:23) MOVQ CX, ""..autotmp_4+112(SP)
0x0084 00132 (main.go:23) JMP 134
0x0086 00134 (main.go:23) PCDATA $0, $0
0x0086 00134 (main.go:23) TESTB AL, (AX)
0x0088 00136 (main.go:23) MOVL go.itab."".User,"".InterUser+16(SB), AX
0x008e 00142 (main.go:23) MOVL AX, ""..autotmp_6+52(SP)
0x0092 00146 (main.go:23) CMPL AX, $2002392420
0x0097 00151 (main.go:23) JEQ 158
0x0099 00153 (main.go:23) JMP 527
0x009e 00158 (main.go:23) PCDATA $0, $1
0x009e 00158 (main.go:23) LEAQ go.itab."".User,"".InterUser(SB), AX // 获取User的itab信息
0x00a5 00165 (main.go:23) PCDATA $0, $0
0x00a5 00165 (main.go:23) CMPQ ""..autotmp_4+104(SP), AX //判断i是不是User类型
...
0x0171 00369 (main.go:23) CALL runtime.assertI2I2(SB) //判断i是不是InterUser类型
...
通过汇编可以看出,非空类型的接口进行断言的时候,如果断言的类型是一个具体的类型比如User,将会比较itab字段,如果断言是一个接口类型比如InterUser会调用runtime.assertI2I2