Notes

A Better Go Defer

October 20, 2020

Go has a defer statement built into the language which allows a function to be executed at the end of another function. This is pretty useful in particular for cleanup (e.g. closing a file handle) or for recovering from errors (because Go code usually contains a lot of if err != nil { return err } and scattering cleanup code everywhere can be visually distracting).

// Real working go code
// Prints out "Start\nmain\nClose
// https://repl.it/@varsnap/Go-Defer-Example
package main

import "fmt"

type Instrumenter struct {}

func (i *Instrumenter) Start() *Instrumenter {
  fmt.Println("Start")
  return i
}

func (i *Instrumenter) Close() {
  fmt.Println("Close")
}

func main() {
  i := Instrumenter{}
  defer i.Start().Close()
  fmt.Println("main")
}

The funny thing is, although Go has first-class support for functions, defer statements take a a function call as an argument, not just a function declaration or function name - i.e. defer run() and not defer Run. While this may be a minor annoyance while programming, it can be pretty unintuitive when combined with a factory pattern that might use defer with a double function call.

I humbly propose (with heavy doubt it’ll be implemented) that the defer syntax be changed so that defer accepts a function rather than a function call as an argument, thereby making defer accept a continuation that can be invoked at the end of a function call (this does cause some problems with variable mutations that may happen later on, which become even more complicated when exception handling is introduced, but hopefully it can be worked out). I also propose that defer be changed from a built-in statement into a function that can accept a continuation as a parameter.