Reputation: 34071
i try to learn golang and use effective go as lecture. I stuck on capital defer, look at the following code
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
as output i've got
entering: b
in b
entering: a
in a
leaving: a
leaving: b
I know, that defer statement will be execute after return statement in the function. But here, why entering: b is the first output? I am expected in b as the fist output!
Upvotes: 2
Views: 581
Reputation: 5666
According to http://golang.org/doc/effective_go.html#defer:
The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes, not when the call executes. Besides avoiding worries about variables changing values as the function executes, this means that a single deferred call site can defer multiple function executions.
So argument of defer function (trace("b") in your case) evaluates first
Upvotes: 8
Reputation: 166569
Simplifying,
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
}
func main() {
b()
}
Output:
entering: b
in b
leaving: b
Since "arguments to deferred functions are evaluated when the defer
executes", the
defer un(trace("b"))
statement in function b
is, using explicit scope, equivalent to
{
unarg := trace("b")
defer un(unarg)
}
Therefore, equivalently,
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func b() {
{
unarg := trace("b")
defer un(unarg)
}
fmt.Println("in b")
}
func main() {
b()
}
Output:
entering: b
in b
leaving: b
Equivalently, for your complete example,
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
{
unarg := trace("a")
defer un(unarg)
}
fmt.Println("in a")
}
func b() {
{
unarg := trace("b")
defer un(unarg)
}
fmt.Println("in b")
a()
}
func main() {
b()
}
Output:
entering: b
in b
entering: a
in a
leaving: a
leaving: b
Upvotes: 2