package main
//go:noinline
//go:nosplit
func test() (int, int) {
return 1, 2
}
func main() {
x := 100
_ = x
a, _ := test()
println(a)
}
对于这个空标识符,编译器是如何处理的?什么类型的变量都可以接收?
我们反汇编来看下具体的处理细节。
root@000d3fada0b3:~/go/src/test# go build -gcflags "-N -l"
root@000d3fada0b3:~/go/src/test# go tool objdump -S -s "main\.main" test
TEXT main.main(SB) /root/go/src/test/main.go
func main() {
0x44c180 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
0x44c189 483b6110 CMPQ 0x10(CX), SP
0x44c18d 7651 JBE 0x44c1e0
0x44c18f 4883ec30 SUBQ $0x30, SP
0x44c193 48896c2428 MOVQ BP, 0x28(SP)
0x44c198 488d6c2428 LEAQ 0x28(SP), BP
x := 100
0x44c19d 48c744241064000000 MOVQ $0x64, 0x10(SP)
a, _ := test()
0x44c1a6 e8a5ffffff CALL main.test(SB)
0x44c1ab 488b0424 MOVQ 0(SP), AX
0x44c1af 4889442420 MOVQ AX, 0x20(SP)
0x44c1b4 4889442418 MOVQ AX, 0x18(SP)
println(a)
0x44c1b9 e84259fdff CALL runtime.printlock(SB)
0x44c1be 488b442418 MOVQ 0x18(SP), AX
0x44c1c3 48890424 MOVQ AX, 0(SP)
0x44c1c7 e8b460fdff CALL runtime.printint(SB)
0x44c1cc e8bf5bfdff CALL runtime.printnl(SB)
0x44c1d1 e8aa59fdff CALL runtime.printunlock(SB)
}
0x44c1d6 488b6c2428 MOVQ 0x28(SP), BP
0x44c1db 4883c430 ADDQ $0x30, SP
0x44c1df c3 RET
func main() {
0x44c1e0 e88b83ffff CALL runtime.morestack_noctxt(SB)
0x44c1e5 eb99 JMP main.main(SB)
通过上面的输出信息可以看到,_ = x
并没有被编译器处理,直接被忽略了。
a, _ := test()
0x44c1a6 e8a5ffffff CALL main.test(SB)
0x44c1ab 488b0424 MOVQ 0(SP), AX
这个空标识符也是仅仅处理了一个变量a
。空标识符也是被忽略掉了。
编译器会忽略空标识符赋值。
小技巧
Go里面很难知道,一个变量是否实现了某个接口。我们可以通过_
空标识符来检查是否实现了某个对应的接口。
例:
package main
type Xer interface {
A()
}
type X int
// func (x X) A() {}
var _ Xer = X(0)
func main() {
}
我们可以通过对空标识符赋值,来让编译器帮我们检查变量X
是否实现了Xer
接口
root@000d3fada0b3:~/go/src/test# go build -gcflags "-N -l"
# test
./main.go:9:5: cannot use X(0) (type X) as type Xer in assignment:
X does not implement Xer (missing A method)
总结
编译器会忽略空标识符赋值。
可以通过空标识符来让编译器帮我们检测一个变量是否实现了某个接口。
网友评论