Go interfaces and type assertions
Marco Franssen /
10 min read • 1852 words
In this blog I would like to zoom in on Interfaces and type assertions in Go. Compared to language like c#
and Java
implementing interfaces works slightly different. In the remainder of this blog I want to give you a bit of theory and practical usecases. In case this is your first time working with Go you might want to check out this blog which shows you how to setup your development environment including a small hello world.
The empty interface (interface{}
) is an interface which defines zero methods. An empty interface may hold values of any type. Any type is at its basics a type without methods. It is used by code that handles values of unknown type. E.g. fmt.Print
takes any number of arguments of type interface{}
. This is the least valuebale interface in terms of reusing logic, but could come very handy in some situation where you simply do not know the type up front. It comes with a cost of not having type safety at the places you use this empty interface.
Now lets have a look at how we can define our own interface. A interface in Go is defined as a set of method signatures. In Go interfaces are implicit. So there is no need to define on a given type that it implements a certain interface. The advantage of this is that the definition of an interface is decoupled from its implementation which could then appear in any package without prearrangement. Later on we will zoom in on this decoupling further.
First lets have a look at an example of the definition of an interface and how a given value type can implement an interface.
When running this you will see following output.
At line 5 we defined an interface with a single method Call() string
. This interface is implicitly implemented on line 17 and 21. The syntax we see over there is what we call a receiver method. In this case it is a receiver which receives a pointer to Cow (*Cow
) and a pointer to Dog (*Dog
). These receivers you could compare to Object.prototype.myMethod
in Javascript
to build a small bridge to a language you might have more experience with. As you can see there is no such thing like we have to do in c#
or Java
where you have to define on the class which interface you are implementing. Due to the fact both our Cow and our Dog implement the Animal interface we now have the ability to create a slice of Animals. Then we simply loop on the slice of animals to have them all call out their own names.
Type assertions
With type assertions we can get access to an interface underlying value. c := a.(Cow)
will assign the underlying value Cow of the animal interface to the variable c. If a is not a Cow this will cause a panic. So in general we will not user this syntax to prevent panics. Instead we will use the following syntax which tests if the interface value holds a certain type.
In above example I have been using the empty interface. With above example you will notice that ok will hold a boolean value which you can use in an if statement for example. s
, f
and b
would hold the underlying value of the underlying type, if the assertion succeeds. Also notice the last assertion we are not doing a test, which causes a panic. In case you want to know more on how to handle panic, you can have a look on this blogpost which has some examples on recovering from a panic.
As a handson example you could try to apply type assertions on the first example in this blogpost. Try to get access to the underlying value of the animals in the for loop to print the underlying value.
Example using type assertions for errors
So now we know a bit on how to work with interfaces in Go I would like to highlight a nice usecase to define your own Error structs and why to use them. In Go error
is also an interface. You will see many packages have methods with signature like this func SomeMethod() error
or func SomeMethod() (string, error)
.
In general we could implement such a method as following and then start testing for the error message string.
As you can see the tests like this would be pretty clumsy and take pretty much effort to implement these kind of tests. E.g. In the tests I don't want to really care for the exact error message. I rather just would like to test if I got an error of a certain type. The error interface in Go looks as following.
So in order for us to define our own error
types we just have to implement the Error() string
function on our struct. So lets have a look at a rewrite of above example where we use our own error
type and rewrite the test to use type assertions.
As you can see this makes your tests a lot more clean. On line 59 you see we are using a type assertion to check if we are getting a FetchError
. You could do similar assertions in your production code to handle each type of error differently. Imagine you would have to make those kind of decisions based on the error string all over the place. That would become a disaster.
In case you want to learn more about testing your Go code you can also have a look at following blog post, which zooms in a bit more on testing and benchmarking your code.
Bonus
Type assertions solution for the animals assignment I gave you earlier.
Using the switch approach we could extend the behavior of our code. The variable a
will be of the matched type as you can see when running the code in the playground. So imagine a Cow would have additional methods you could do something with those methods in the switch case for Cow. Also notice on the getSpecies method I have used the empty interface (interface{}
). In this case we also could have used our Animal
interface ofcourse as we only use it on our animals, but imagine we would pass something else then animal this function would still work. In general I like to avoid the empty interface as it doesn't allow for compile time checks. However there will be usecases where you will need them, as there are no generics in Go yet. For Go 2 there are plans for generics as well contracts
that would add compile time checks for usecases we now need to implement using interfaces and type assertions.
Thanks for reading. Hopefully this clarified a bit on the awesomeness of interfaces in Go which I really like as they are more loosely coupled then in other programming languages. I would appreciate it if you could share my blog on social media. Comments are also very welcome.