albertyw/albertyw.com

View on GitHub
app/notes/20201021-0544.md

Summary

Maintainability
Test Coverage
A Better Go Defer

a-better-go-defer

1603259076

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).

```go
// 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.