I've 2 code examples:
func test() int {
var x int
defer func() {
x++
}()
x = 1
return x
}
func main() {
fmt.Println(test())
}
It returns: 1
. However, the following code example behaves differently:
func test() (x int) {
defer func() {
x++
}()
x = 1
return
}
func main() {
fmt.Println(test())
}
It returns 2
.
In order to understand what's going on, I disassembled the code.
For the first code snippet (whose output is 1
):
main.go:19 0x48cf40 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
main.go:19 0x48cf49 483b6110 CMPQ 0x10(CX), SP
main.go:19 0x48cf4d 7678 JBE 0x48cfc7
main.go:19 0x48cf4f 4883ec58 SUBQ $0x58, SP
main.go:19 0x48cf53 48896c2450 MOVQ BP, 0x50(SP)
main.go:19 0x48cf58 488d6c2450 LEAQ 0x50(SP), BP
main.go:20 0x48cf5d e83effffff CALL main.test(SB)
main.go:20 0x48cf62 e8c9bff7ff CALL runtime.convT64(SB)
main.go:20 0x48cf67 488b442408 MOVQ 0x8(SP), AX
main.go:20 0x48cf6c 0f57c0 XORPS X0, X0
main.go:20 0x48cf6f 0f11442440 MOVUPS X0, 0x40(SP)
main.go:20 0x48cf74 488d0d05090100 LEAQ 0x10905(IP), CX
main.go:20 0x48cf7b 48894c2440 MOVQ CX, 0x40(SP)
main.go:20 0x48cf80 4889442448 MOVQ AX, 0x48(SP)
print.go:274 0x48cf85 488b0524020d00 MOVQ os.Stdout(SB), AX
print.go:274 0x48cf8c 488d0d0dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
print.go:274 0x48cf93 48890c24 MOVQ CX, 0(SP)
print.go:274 0x48cf97 4889442408 MOVQ AX, 0x8(SP)
print.go:274 0x48cf9c 488d442440 LEAQ 0x40(SP), AX
print.go:274 0x48cfa1 4889442410 MOVQ AX, 0x10(SP)
print.go:274 0x48cfa6 48c744241801000000 MOVQ $0x1, 0x18(SP)
print.go:274 0x48cfaf 48c744242001000000 MOVQ $0x1, 0x20(SP)
print.go:274 0x48cfb8 e84397ffff CALL fmt.Fprintln(SB)
print.go:274 0x48cfbd 488b6c2450 MOVQ 0x50(SP), BP
print.go:274 0x48cfc2 4883c458 ADDQ $0x58, SP
print.go:274 0x48cfc6 c3 RET
main.go:19 0x48cfc7 e8d447fcff CALL runtime.morestack_noctxt(SB)
main.go:19 0x48cfcc e96fffffff JMP main.main(SB)
For the second code snippet (whose output is 2
):
main.go:18 0x48cf30 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
main.go:18 0x48cf39 483b6110 CMPQ 0x10(CX), SP
main.go:18 0x48cf3d 7678 JBE 0x48cfb7
main.go:18 0x48cf3f 4883ec58 SUBQ $0x58, SP
main.go:18 0x48cf43 48896c2450 MOVQ BP, 0x50(SP)
main.go:18 0x48cf48 488d6c2450 LEAQ 0x50(SP), BP
main.go:19 0x48cf4d e84effffff CALL main.test(SB)
main.go:19 0x48cf52 e8d9bff7ff CALL runtime.convT64(SB)
main.go:19 0x48cf57 488b442408 MOVQ 0x8(SP), AX
main.go:19 0x48cf5c 0f57c0 XORPS X0, X0
main.go:19 0x48cf5f 0f11442440 MOVUPS X0, 0x40(SP)
main.go:19 0x48cf64 488d0d15090100 LEAQ 0x10915(IP), CX
main.go:19 0x48cf6b 48894c2440 MOVQ CX, 0x40(SP)
main.go:19 0x48cf70 4889442448 MOVQ AX, 0x48(SP)
print.go:274 0x48cf75 488b0534020d00 MOVQ os.Stdout(SB), AX
print.go:274 0x48cf7c 488d0d1dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
print.go:274 0x48cf83 48890c24 MOVQ CX, 0(SP)
print.go:274 0x48cf87 4889442408 MOVQ AX, 0x8(SP)
print.go:274 0x48cf8c 488d442440 LEAQ 0x40(SP), AX
print.go:274 0x48cf91 4889442410 MOVQ AX, 0x10(SP)
print.go:274 0x48cf96 48c744241801000000 MOVQ $0x1, 0x18(SP)
print.go:274 0x48cf9f 48c744242001000000 MOVQ $0x1, 0x20(SP)
print.go:274 0x48cfa8 e85397ffff CALL fmt.Fprintln(SB)
print.go:274 0x48cfad 488b6c2450 MOVQ 0x50(SP), BP
print.go:274 0x48cfb2 4883c458 ADDQ $0x58, SP
print.go:274 0x48cfb6 c3 RET
main.go:18 0x48cfb7 e8e447fcff CALL runtime.morestack_noctxt(SB)
main.go:18 0x48cfbc e96fffffff JMP main.main(SB)
The Golang spec says this about defer statements:
[...] if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.
(my emphasis)
In your first snippet, function test
has no named return parameter; x
is merely a local variable. Therefore, your defer statement cannot modify the result of function test
.
In your second snippet, function test
has a named return parameter x
, which is in scope within your function literal. Therefore, the defer statement can (and does) modify the result of function test
.