Interfaces in Golang

A short tutorial on interfaces in Go

Darko Kantic
4 min readAug 10, 2021

The programing language Go, developed by Google, was designed to be easy to understand and learn. However, their interfaces implementation is not always easy to grasp. In this article, I will explain interfaces in Go using a good old Shapes example.

Assignment: Create a command-line program to calculate the surface of geometric shapes: triangle, square, and circle.

Download the complete code from GitHub

By using interfaces you can generalise number of related types. Photo by Kelly Sikkema on Unsplash

Implementation Without Interfaces

First, we implement our assignment in Go without using an interface, then we will see how an interface can help us reduce the code and simplify the program.

Create three types with associated functions

First, let's create Circle in a file named circle.go:

We create a struct with just one field: a radius. Then we create a factory function that will create an instance of a circle type. The readFloat() is a utility function used to capture user input from the terminal, we declare it in the main.go file, you will see this later.

When looking at the circle.go file some people might be tempted to think that the content of the file looks like a Circle class. I would say to them to be careful as the Go Puritans Police 😃 are watching. This is because Go is not an OO programing language and does not have objects or classes. (To be honest, the circle.go file reminds me a lot of a Circle class, but don’t tell anyone I told you that 😃)

Instead of having methods inside the type (which would make it a class), the Go types have a function associated with them using method receivers.

Both functions: getArea() and printArea() are method receivers and the part in brackets before the function name, like (c Circle), denotes the type associated with the function, in this case, the type is Circle.

Let us declare square and triangle in a similar fashion:

We make use of these types in the main.go file:

This program is now complete and can calculate the surface of the three shapes.

However, we notice that the printArea() function for each of the three shapes is very similar. How can we refactor the program to avoid such repetition?

Implementation Using Interfaces

We can reduce repetition by using only one generic printArea() function. But how can we get all three types to use it? We need to generalise all three shapes into some kind of generic object shape. Then we‘d be able to print the surface of each shape by calling the same function and passing in the type.

The generic function would look like:

How can we do this?

Interface to the rescue.

We need a shape interface that will generalise all three shapes by giving them a common interface to the external world.

Let's declare the interface:

Now how to make our three shapes implement this interface? To do this, we do … nothing!

What!?

Yes, that's right, nothing. This is one of most peculiar things about Go, the compiler itself will do the work for you. In other languages you would need to write something like:

type Triangle struct implements Shape{

But not in Go, because all three shapes have the method receiver getArea() that returns float64, the Go compiler will implicitly make these types implement the shape interface. This is so-called Duck Typing: “If it walks like a duck and quacks like a duck, it must be a duck.”

You can think of receiver methods as class methods if you are thinking in OO fashion, and because each of the shapes already have the getArea() method with the right signature, Go will automatically assume that these types implement the shapes Interface.

The signature has to be exactly right, if one of the shapes, say triangle, had a receiver with the following signature:

func getArea() float32

then Go will not automatically assume that the shape implements the interface, this is because the signature of the method is different. The function returns float32 instead of float64.

Have designers of Go language made a good or a poor decision? It depends, it does make it harder to work out which type implements which interface, at least initially. However, it does save you some typing and therefore reduces the code. In time, you will train your eye to quickly spot this, so perhaps it is not a bad decision after all.

I like to put the generic function and interface in the same file, but this is up to you:

We remove the printArea() function from each shape and change the main function:

For example, instead of

c := createCircle()c.printArea()

we write

c := createCircle()printArea(c)

That's all, we reduced our code and instead of three functions we have one generic function. Also, if we want to add another shape, say rectangle, we would not need to modify the printArea() function as long as our new shape implements the Shape interface.

To conclude, Go is not a fully Object Oriented language, but with this and a few other features, to me, it is sufficiently Object Oriented.

Download the complete code from the GitHub

--

--