defer fun()

deferred dreams by torbakhopper

After programming Go for more than two years my favorite Go keyword is defer. I might go into detail about this in a future post, but this is one of the nice things I found. About a year ago I came across a funny little Go-ism when I was looking through some code. It was written like this:


var mu sync.Mutex

func somefunc() {
    defer Un(Lock(&mu))

    // ...
} 

I was intrigued by the simplicity of this, and by the time I didn’t know the all the fine details of defer, mainly the fact that when you defer a function the function parameters are evaluated immediately.

For those who don’t know the defer keyword in Go, it will execute the function after when the current function returns. In this case it will call the Un function with the parameter returned by the Lock function whenever somefunc returns.

In this case this is how Un and Lock is implemented:


func Un(f func()) {
    f()
}

func Lock(x sync.Locker) func() {
    x.Lock()
    return func() { x.Unlock() }
}

Very elegant. A variation could also just return the sync.Locker interface, but returning a function is a bit more flexible. This even has the advantage that you can even use it to read-lock an RWMutex, by calling the RLocker function to get the read lock/unlock as a Locker interface. It would look like this:


var mu sync.RWMutex

func somefunc() {
    defer Un(Lock(mu.RLocker()))
    // ...
}

I like it, because it is very readable and memorable, and still clearly shows the intention of your code. The difference between this and a standard two-line Lock + defer Unlock is mostly ‘sugar’.

I haven’t used it much in real code for multiple reasons. First of all, you might need to unlock manually in case you need to make a function call that require the lock, for instance:


func somefunc() {
    defer Un(Lock(&mu))

    // Recursive call deadlocks
    somefunc()
}

Even if your calling function doesn’t lock the mutex, you might still want to release it when calling the function. This is only relevant if you have a high-contention lock, or your call is expected to take a long time. Another (minor) thing is that `defer` isn’t completely free, and while the performance overhead can be minor. An alternative is using a function with a closure that performs the locking/unlocking.

This was just to show one of the uses of defer. If you have seen some cool/fun use of defer, like this crazy string reversal, feel free to share them in the comments.