The empty interface in the Go programming language

Care to share? ...Share on TumblrShare on LinkedInShare on RedditShare on FacebookShare on Google+

One of the most hotly debated topics in the world of the Go programming language, is the lack of generics. Generics are considered a key feature in other statically typed programming languages like Java or C#. However, the makers of Go resisted the demand to add it to the language so far. A key reason why Go’s makers and developers could utilize the language in complex production software without the presence of generics, is because of the “empty interface” feature, which helped fill some of the gaps left by the lack of generics in Go.

Generics, an overview

First things first, let’s start this correctly: What exactly are generics and why are they important?

The governing principle behind generic programming is that you write your code with ‘types to be specified later’. This basically means that you don’t specify your object types while  writing your code, but instead you use ‘templates’ or ‘placeholders’ to be later replaced with your types. At compile time, the compiler can perform proper type checks.

Let’s explore an example in Java where we make use of generics. We’ll write a class called genericsdemo, which will host two methods: storeValue and printStoredValue.

The storeValue method will take a value of ANY type, then store it in a variable. The value could be an integer, a string, a custom object, whatever your heart pleases. Whereas the printStoredValue method will print the stored value.

Here is how the code will look like:

In the above code we use generics to allow the class to accept a value of any type. The key here is to use the <T> syntax in our class declaration. This allows us to then use as the placeholder for our still unknown type.

Now let’s look into how we would use the genericsdemo class:

In the above code, we first create an instance of the genericsdemo class, where we specify the data type of <T> to be a double. Then, we store a value of 44.5. At the end, we print the stored value.

We then repeat the same logic, except this time we instantiate the genericsdemo class with a String type instead of a double.

If I run the code, I get an output that looks like this:

So obviously our code accepted both data types (float and string) perfectly fine without complaining, and here -my dear reader- is where the power of generics lies for statically typed languages.

You can see how much power and flexibility generics can offer  you as a developer. It is extremely popular with SDKs and API libraries, as it allows you to write a single piece of code that can support multiple different types in one go.

Generics vs Go

Now let’s move back to the Go language. Go does not support generics in any official capacity. This means that the lovely piece of Java code we covered above has no parallels in the Go language. But then what to do when we need to write a piece of code that is expected to support different data types at the same time? This is where the concept of the empty interface comes in. Unfortunately, it doesn’t take us all the way there, however it offers a nice ‘middle of the road’ solution to the problems that generics in other languages attempt to solve.

One very important remark to note though, is that we shouldn’t rely on the empty interface on every single situation. Overusing the empty interface can result of unreadable or unpredictable code on the long run. However, using it with just the right dosage should be perfectly fine. So make sure you keep that in mind whenever you consider writing a new piece of code with the empty interface.

What is the empty interface?

So what exactly is the empty interface in the Go programming language? It is exactly what the name says: it’s an empty interface:

But why is it special? The answer rests on the fact that in the Go programming language, implementing an interface is implicit and not explicit. In other words, there is no implements keyword like in Java to connect an interface with a type that implements it. Instead, Go will look for the types in your code that implement the same methods as the interface, then implicitly connect those types to the said interface.

For example, if I wrote an interface that looks like this:

Then, I wrote a struct type that looks like this:

The mystruct type becomes automatically a child of the myinterface interface, no special keywords needed. This means we can use myinterface in place of mystruct in our code, like this:

In the above code testfunction takes an argument of type myinterface.  However, since mystruct is a child of myinterface, the testfunction function will happily accept a value of type mystruct as an argument.

The caveat here though, is that in order to get back a value of type of mystruct from a value of type myinterface, we need to use a Go feature called ‘Type assertion’. Here is how this would look like:

In the above code, we used type assertion to extract mystruct from it’s parent interface myinterface. 

Type assertion is a useful feature in Go that involves extracting a concrete type from it’s parent interface. Because of this, you can only use Type assertions on interfaces.

This implicit relationship between myinterface and mystruct is what makes the empty interface special. Since the empty interface doesn’t include any methods, then in practice, any Go type is implicitly a child of the empty interface. In other words, we can substitute any Go type with the empty interface. Consider the testfunction example again, it could have been written as follows with the same result as before:

However, since the empty interface type could accept any type and not just mystruct, we can substitute it with any other Go type as we please:

In the above code, we made use of the empty interface to act as an int type in one case, and a string type in another.  This all looks nice, but I am sure you would agree with me that the code could use more elegance! Luckily Go comes to the rescue with another feature called ‘type switch’, here is how this would look like:

As you can infer from the above code, the ‘type switch’ feature is a simple switch statement that is capable of switching between datatypes. In our case, we had two options, either a string type or an int type. The code above will produce the same results as the other code snippet that made use of the testfunctionstring, and testfunctionint functions.

Perfect, with this, you should have a good idea of what the empty interface in golang is about, and why it usually gets mentioned in situations where generics are expected to help. Most of Go APIs make use of the empty interface to accept multiple types.

Consider the fmt package for example, a very popular function in the package is fmt.Println which is used to write a new line to the standard output. Most of us use this function whenever we want to print out a quick line to debug a program or see an output. If you dive deeper into this function, you’d find that the function signature looks like this:

The function takes a dynamic list of arguments, all of them are of the empty interface type. This is why when I type fmt.println(“hello”,4) , I get no complains about unmatched types.

Now comes the important question: why does everybody complain about the fact that Go does not have decent support for generics, if we can make use of the power of the empty interface, then go on to live happily ever after?

The answer is simple: In almost all cases, when we make use of the empty interface in Go, we have to convert the value back to the original type at runtime in order to obtain the original value (remember how we had to use type assertion or type switch?). This is similar in principle to the concept of ‘boxing’ in other languages, where we can use a parent type (type ‘Object’ for example in Java) to represent all child types, then figure out at runtime how to get the child from parent. This typically comes with some price in performance.

Generics, on the other hand, don’t work that way. For generics, the type checking happens at compile time, and has no visible overheads at runtime.

There is an interesting stackoverflow thread discussing the performance of type assertions, type castings, and type switches in Go , they have been steadily improving over the years. However, the fact still stands that you have to design your code in a way to expect a type conversion somewhere after the usage of the empty interface: https://stackoverflow.com/questions/28024884/does-a-type-assertion-type-switch-have-bad-performance-is-slow-in-go

Some people think that Go doesn’t need full generics support, while others very strongly disagree. The purpose of this article is not to prove one point of view is right or wrong,  but rather to shed some light into how you can utilize the empty interface in your code to build more flexible software.

Enjoyed this article? check out my new online course: Modern Golang Programming, where we cover how to build powerful and modern applications in the Go language.

Care to share? ...Share on TumblrShare on LinkedInShare on RedditShare on FacebookShare on Google+

4 thoughts on “The empty interface in the Go programming language”

  1. An interesting article – I can’t say I use Go, but I can see how the empty interface could in most cases take the place of generics.

    On the other hand, there’s one important thing I think you missed; a big part of the power of generics is in the ability to define constraints. For example, in Scala:

    class PetSitter[T <: Animal]() {

    }

    This would define a class with a generic type that can take any subclass of Animal. One's first thought might be to simply use Animal in the first place instead of a generic; however, by using a generic, you gain the ability to write methods that return the underlying type, rather than the superclass (which would then require typecasting, which quickly becomes clunky and unsafe). By doing this, you retain the type safety and flexibility of the system (you know 100% that you're dealing with an Animal, so you can do anything to it that you can do to an Animal) without losing the expressiveness and type safety later (you also know what type of Animal, rather than just that it is one, without having to check manually).

    I could be missing a piece of the puzzle here, but it doesn't seem to me like Go really answers in kind in this regard. You could define an interface with the methods you need attached in order to restrict the type of thing you're dealing with, but that still leaves open the possibility that you're not in fact dealing with an Animal at all, but rather some other thing that happens to be able to breathe, eat, and sleep. Generics, on the other hand, provide an "is a" check as opposed to a "looks like a" check, which makes them much more conceptually effective and powerful.

    That said, I've never actually used Go, so this is all me just speculating on the rough concepts. If I've missed something, please feel free to let me know =)

    1. Thank you very much for your feedback, constraints in generics is a very good point indeed.

      Shaban’s comment did a great job in shedding the light into how this would be handled in Go, which is by carefully defining simple interfaces that we require our types to follow.

      You are probably thinking now that this is clunky and unsafe due to the need in some cases to convert the interface back to the concrete object that it holds. In Go, when you convert from a parent interface to a child type, you use ‘type assertion’, which is different than type casting. Type casting is just casting type t1 to type t2 (similarly to type casting in Java or C# among others) , whereas type assertion involves extracting the concrete type hiding behind the interface then returning it. Type assertion also supports safety by allowing you to test if the targeted interface wraps around the concrete type or not (EDIT: To clarify here, the default behavior would be to throw a panic if we try to type assert into the wrong type, if we would like to avoid the panic, then we use a slightly different syntax to return a boolean flag indicating whether the type assertion was successful or not). Type assertion is definitely an extra step needed to get back your concrete type at run time, however people have used the feature in very complex software with great success.

      Please check out this link: https://tour.golang.org/methods/15 for more context.

      Hope that was helpful 🙂

  2. @Connor
    afaik this is possible.

    type Animal interface{
    Move()
    }
    type Crocodile interface{
    Move()
    Bite()
    }

    type Bird interface{
    Move()
    Fly()
    }

    All these interfaces fulfill Animal and thus can work the way you want it to work.

Leave a Reply

Your email address will not be published.