Deferring functions for "on entry" actions
I decided to learn Go because it seems an interesting and easy to learn low-level programming language. There are several free online resources to learn the language, such as The Go Tour, but I decided to Go for the book "The Go Programming Language" (GOPL) by Alan Donovan and Brian Kernighan. In the book, there are some code examples that I find particularly elegant. Here, I want to record some of these examples for future reference -- while having the chance to add some additional comments on the code for myself.1
In Go there exists the defer
statement to execute a function when the parent
function exits. For example, this is useful when you want to defer the closing
of a file that you opened:
func main() {
file, err := os.Open("file.go")
if err != nil {
log.Fatal(err)
}
defer file.Close()
}
But defer can also be used to execute a code on both exit and entry, e.g.:2
func bigSlowOperation() {
defer trace("bigSlowOperation")() // don't forget the extra parentheses
// ...lots of work...
time.Sleep(10 * time.Second) // simulate slow operation by sleeping
}
func trace(msg string) func() {
start := time.Now()
log.Printf("enter %s", msg)
return func() { log.Printf("exit %s (%s)", msg, time.Since(start)) }
}
The secret lies in two aspects of the code above: First, trace
returns an
anonymous function. Second, the anonymous function is run by the extra
parenthesis in bigSlowOperation
. When the function is executed, trace
executes because of the extra parenthesis, which initialises variable start
and prints msg
. The anonymous function is returned and handed over to defer
which will execute it as soon as bigSlowOperation
is exited. Eventually this
prints the exit message.
The original source code of the examples can be found in the book's GitHub repository. The code is licensed under CC BY-NC-SA. 2: The example is taken from gopl.io/ch5/trace.