
Gotcha with defer in Go
13 Jan 2021 #pattern #golang #development
Today, I wanted to add some timings to a function call in Go.
I wanted something very simple, so I opted for:
package main import ( "fmt" "time") func main() { start := time.Now() defer fmt.Println("ELAPSED:", time.Since(start)) time.Sleep(2 * time.Second) }
When running this, I was a bit surprised I got the following result:
$ go run defertester1.goELAPSED: 186ns
I was expecting to see an elapsed time of at least 2 seconds.
The caveat here is that as soon as the defer
statement is called, it's already evaluating the result of fmt.Println("ELAPSED:", time.Since(start))
.
After a search on StackOverflow, here's a nice description of why the result is like this:
The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns. For example,
defer outcall(incall())
, theincall()
is evaluated immediately, while theoutcall()
is not executed until the surrounding function returns.
To get the correct result, you need to wrap this in a function call so that it only gets evaluated when the defer
statement is executed.
package main import ( "fmt" "time") func main() { start := time.Now() defer func() { fmt.Println("ELAPSED:", time.Since(start)) }() time.Sleep(2 * time.Second) }
When we execute this, we now get the expected result:
$ go run defertester2.goELAPSED: 2.004822262s