Let\'s assume I have the following function
func printNumbers(){
var x int
defer fmt.Println(x)
for i := 0; i < 5; i++{
x++
}
}
If the defer has arguments they are evaluated at the line of the defer-statement; this is illustrated in the following snippet, where the defer will print 0:
func printNumber() {
i := 0
defer fmt.Println(i) // will print 0
i++
return
}
You can use an anonymous function as a defer statement if you want to postpone the execution of a statement or a function until the end of the enclosing (calling) function. Here is an updated example:
func printNumbers() {
x := 0
defer func() { fmt.Println(x) }()
for i:=0; i < 5; i++ {
x++;
}
return
}
http://play.golang.org/p/YQGQ_8a0_9
Generally what is important is x
cannot be a parameter of the function you are deferring, because they are evaluated when defer
is executed.
Here's a solution using an anonymous function:
defer func() { fmt.Println(x) }()
Here x
is not a parameter of the deferred anonymous function, so it will not be evaluated. Only when the anonymous function is executed and it calls fmt.Println()
.
Using a pointer (like &x
) pointing to x
would work, because only the address is evaluated, and the pointed value at the end will be 5
of course. The problem with this is that fmt.Println()
will not print the pointed value but the pointer itself.
But to demonstrate its working, see this helper function:
func Print(i *int) {
fmt.Println(*i)
}
And using it:
defer Print(&x) // Will print 5 at the end
This is similar to the pointer solution, but doesn't need a helper function. But it does need you to write your String()
method:
type MyInt int
func (m *MyInt) String() string {
return strconv.Itoa(int(*m))
}
And using it:
var x MyInt
defer fmt.Println(&x)
for i := 0; i < 5; i++ {
x++
}
When defer
statement is executed, only the pointer will be evaluated (address of x
, type of *Myint
). And since the type *MyInt
implements fmt.Stringer, fmt.Println()
will call its String()
method.
This is also similar to the pointer solution, and this will not even print exactly just 5
as you expect, but:
Problem with #2 was that fmt.Println()
will print the pointer and not the pointed value (which we solved with our own Print()
function). However there are other types which are similar to pointers and fmt.Println()
will print their content.
So let's wrap the variable into a slice, and see what happens:
x := []int{0}
defer fmt.Println(x)
for i := 0; i < 5; i++ {
x[0]++
}
Prints:
[5]
Reason for seeing 5
is that a slice is a descriptor. When defer
is evaluated, a copy is made of the slice (that will be passed to fmt.Println()
when it gets executed) but it refers to the same underlying array.
Also note that fmt.Println()
prints the pointed content if the pointer is a pointer to a struct, array, slice, maps, so the following code also works:
x := struct{ i int }{}
defer fmt.Println(&x)
for i := 0; i < 5; i++ {
x.i++
}
And prints:
&{5}