Golang Acquire and Release Pattern

John Griffin
Jul 30, 2017 · 2 min read

Moving a finance library from C++ to Golang has been generally a positive move, but I can no longer utilize automatically invoked destructors.

In particular, I needed a cached entity representing a chunk of time series. Pulling data from the cache and putting it back after use were the purview of requestCachedObs and release respectively. It’s easy enough, as long as you remember to give back your resource.

observations := requestCachedObs()
defer release(observations)

Not surprisingly, in one place I mistakenly did not call release. This forced a later operation requesting a cached object to received a newly allocated object, lowering performance. More gravely, this object was a slice of length zero and huge capacity. If I return it after an append call, I’m placing bad data back into the cache, waiting for a really bad bug. I really need to call the defer immediately.

Spotting the error later, I considered how in my C++ code, this was handled perfectly by a destructor. Ok, but I’m using Golang here, so how do I assure the call torelease(observations)? The change was to return both the cached observations and the release functionality in the original call.

observations, release := requestCachedObs()
defer release()

Referencing the release function is necessarily required when retrieving the cached observations and if that function is unused, Go won’t compile. Additionally, I’m 100% sure the data going back into the cache is the correct object because release() now a thunk. The general pattern is now:

resource, destructor := constructor(…)
defer destructor()

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade