It seems like there is no difference. We can check in the generated machine code.
Here is a toy program:
package main
import "fmt"
func topLevelFunction(x int) int {
return x + 4
}
func useFunction(fn func(int) int) {
fmt.Println(fn(10))
}
func invoke() {
innerFunction := func(x int) int {
return x + 8
}
useFunction(topLevelFunction)
useFunction(innerFunction)
}
func main() {
invoke()
}
And here is its disassembly:
$ go version
go version go1.8.5 linux/amd64
$ go tool objdump -s 'main\.(invoke|topLevel)' bin/toy
TEXT main.topLevelFunction(SB) /home/vasiliy/cur/work/learn-go/src/my/toy/toy.go
toy.go:6 0x47b7a0 488b442408 MOVQ 0x8(SP), AX
toy.go:6 0x47b7a5 4883c004 ADDQ $0x4, AX
toy.go:6 0x47b7a9 4889442410 MOVQ AX, 0x10(SP)
toy.go:6 0x47b7ae c3 RET
TEXT main.invoke(SB) /home/vasiliy/cur/work/learn-go/src/my/toy/toy.go
toy.go:13 0x47b870 64488b0c25f8ffffff FS MOVQ FS:0xfffffff8, CX
toy.go:13 0x47b879 483b6110 CMPQ 0x10(CX), SP
toy.go:13 0x47b87d 7638 JBE 0x47b8b7
toy.go:13 0x47b87f 4883ec10 SUBQ $0x10, SP
toy.go:13 0x47b883 48896c2408 MOVQ BP, 0x8(SP)
toy.go:13 0x47b888 488d6c2408 LEAQ 0x8(SP), BP
toy.go:17 0x47b88d 488d052cfb0200 LEAQ 0x2fb2c(IP), AX
toy.go:17 0x47b894 48890424 MOVQ AX, 0(SP)
toy.go:17 0x47b898 e813ffffff CALL main.useFunction(SB)
toy.go:14 0x47b89d 488d0514fb0200 LEAQ 0x2fb14(IP), AX
toy.go:18 0x47b8a4 48890424 MOVQ AX, 0(SP)
toy.go:18 0x47b8a8 e803ffffff CALL main.useFunction(SB)
toy.go:19 0x47b8ad 488b6c2408 MOVQ 0x8(SP), BP
toy.go:19 0x47b8b2 4883c410 ADDQ $0x10, SP
toy.go:19 0x47b8b6 c3 RET
toy.go:13 0x47b8b7 e874f7fcff CALL runtime.morestack_noctxt(SB)
toy.go:13 0x47b8bc ebb2 JMP main.invoke(SB)
TEXT main.invoke.func1(SB) /home/vasiliy/cur/work/learn-go/src/my/toy/toy.go
toy.go:15 0x47b8f0 488b442408 MOVQ 0x8(SP), AX
toy.go:15 0x47b8f5 4883c008 ADDQ $0x8, AX
toy.go:15 0x47b8f9 4889442410 MOVQ AX, 0x10(SP)
toy.go:15 0x47b8fe c3 RET
As we can see, at least in this simple case, there is no structural difference in how topLevelFunction
and innerFunction
(invoke.func1
), and their passing to useFunction
, are translated to machine code.
(It is instructive to compare this to the case where innerFunction
does capture a local variable; and to the case where, moreover, innerFunction
is passed via a global variable rather than a function argument — but these are left as an exercise to the reader.)