Struct And Interface Embedding

A walkthrough of Embedding in go version 1.14

Embedding¹ is a clever way of extending/composing many types into a single type. It represents the O² part of SOLID³ principles — Open closed principle; open to extension, closed for manipulation. What this means is that we can easily extend a type so that it does the original thing, as well as something else without having to amend the original implementation.

Sample Code for this Post

Struct Embedding

Note how I’ve not named the fields. If I had then loggerAndChecker would not implement the functions from the other two types implicitly, and I would be required to explicitly call the functions, as in this example:

Also worth noting that loggerAndChecker can contain its own fields along with the embedded structs.

If either or both the logger and checker have a field s, then the s declared in loggerAndChecker would override the s from the embedded structs. Both the s values from the embedded structs are still accessible explicitly. If loggerAndChecker did not declare s but both logger and checker did, we wouldn’t be able to implicitly call s from loggerAndChecker, but we can still retrieve each of the s values from both embedded structs:

Like above, if the logger and checker implement the same function (e.g. validate), and we try and call validate from loggerAndChecker it will not compile. However, if we call any other function that do not overlap it will compile and run:

I think it’s a good idea to override the overlapping method, otherwise it will be very annoying for someone who tries to use that function. In the example below I use the validate function from the logger only:

Interface Embedding

In the following example we have a few interfaces. doer and validator each declare functions that should be implemented for a type to qualify to be identified as one or the other. doerAndValidator doesn’t declare any functions explicitly but instead relies on the declarations of the embedded interfaces.

It’s worth noting that the doesAndValidator can still declare its own functions too as well as embedding other interfaces:

What if the doer and the validator declare the same function? As of go version 1.14, that’s completely fine:

Wait, what about if we two interfaces declare functions with same name, but different signatures? That’s still not ok and it won’t compile since go doesn’t do method overloading like in other languages such as Java or Python:

This won’t compile

Real World Example

You will see that I’ve used struct embedding of http.Client into retryableHTTPClient. We can call all the HTTP client public functions, even if we haven’t implemented them for retryableHTTPClient as they will fall back to using the HTTP client’s implementation. In this example only the GET is retryable, so you would need to complete this for the other public methods that also need to be retryable.

Using interface embedding to extend the builtin http.Client to be retryable

Other Articles That Are Also Worth A Read

References

[2] https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle

[3] https://en.wikipedia.org/wiki/SOLID

[4] https://www.w3schools.com/java/java_methods_overloading.asp

[5] https://github.com/cenkalti/backoff

On an adventure

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store