Go Options Pattern
Flexible Constructors in Go
When I started writing Go, I had been writing Java for a while and I was very used to overloading constructors. While Go “constructors” are really just functions like any other, I didn’t see a good way to initialize a struct in more than one way because Go doesn’t allow overriding functions.
Take the following code for example. We create a contrived client of some kind that needs initialization. We have a couple constructors for two different types and that’s it. The only other way to create a Client
or a Call
would be for the caller to explicitly create it with &client{}
, but in this case, that’s a non-exported struct so it’s not possible. Also, no overloading, right? right.
How do Other Languages Handle this?
Java
As we know, Java allows for overloading of constructors.
Javascript
Javscript does not allow overloading either. From my limited experience with js, it seems that config objects are the way to go. I have read about some hacks to fake overloading, but around here we like readable code.
Rust and Go
Both Rust and Go use struct literals. Personally, I still use the ol’ struct literal in Go far more than I use options, but it’s nice to have another tool in the box.
What are my Other Options?
Here we have an arbitrary type that we have provided options for so the caller can pick and choose what she wants and the the New
function will handle any other initialization. Win/Win, right? I admit, it’s a little hard to read at first, but I think it’s a nice compromise. :)
Basically what is happening is that we have a number of functions that take some parameter and return a function that takes a pointer to a Context. Then on line 43 in NewContext
we loop through all the options passing a pointer to the newly created context so the options functions can make modifications. An added bonus/pitfall is that the called has access to all non-exported variables from inside an options function. You could do some real damage if you wanted to. Then again, you’d have to debug it and the damage would be to your free time and your sanity.
I have used the options pattern a couple times “in the wild”, but like i mentioned, I usuall use a simple func or a stuct literal still. Checkout out Trickster for a real world implementation of Go options that I wrote while adding Open Telemetry support.