Go Options Pattern

Flexible Constructors in Go

Guy J Grigsby
Star Gazers
3 min readFeb 4, 2021

--

The Go Gopher approaches a crossroads sweating. The sign reads “Option 1” and “Option 2”.
Gopher © Renee French. Sweet crossroads by Guy J Grigsby. CC by 4.0

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?

Go Gopher with a construction hat, holding a brick and constructing a short retaining wall
Gopher © Renee French. Used with permission.

Java

As we know, Java allows for overloading of constructors.

Overloading FTW

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.

Config Object

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.

Rust and struct literals
Forgive the Go present annoations. I adapted a talk I wrote for this blog post.

What are my Other Options?

The Go Gopher approaches a crossroads sweating. The sign reads “Option 1” and “Option 2”.
Have another gander at this beauty. I am very proud. Maybe I will write a blog post about learning Illustrator.
Please don’t actually override Context.

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.

--

--

Guy J Grigsby
Star Gazers

Technologist, taco lover and Gopher. Technologist and software engineer in distributed systems. Neuro-divergent.