DEV Community

Tom Deneire ⚡
Tom Deneire ⚡

Posted on • Originally published at towardsdev.com

TIL: Go typecasting with empty interfaces

TIL (“Today I learned”) are shorter, less-researched posts that I typically write to help organize my thoughts and solidify things I have learned while working. I post them here as a personal archive and of course for others to possibly benefit from.

Photo by [Suzanne D. Williams](https://unsplash.com/@scw1217?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)
Photo by Suzanne D. Williams on Unsplash

Empty interfaces

Today I was working with Pigeon, a Go package which creates parsers based on a parsing expression grammar (PEG). As the documentation details, it is possible to use a labeled expression in the definition of the rules, like so:

LabeledExpr = value:[a-z]+ {
    fmt.Println(value)
    return value, nil
}
Enter fullscreen mode Exit fullscreen mode

In the Go code between brackets value is typed as an empty interface (interface{}). When you match a sequence (as in the above with + ), the underlying type is a slice of empty interfaces.

Since an interface is just a set of methods — an empty interface having no methods — , you need to cast these empty interfaces to concrete types (syntax = interface.(type)) in order to access the underlying values.

Let’s illustrate this with some code. First, I define a function — for didactic purposes — that will turn any type into an empty interface.

func makeInterface(object interface{}) interface{} {
    return object
}
Enter fullscreen mode Exit fullscreen mode

Then I make an object which is a slice of empty interfaces (following the example in the above):

object := make([]interface{}, 2)
Enter fullscreen mode Exit fullscreen mode

Then I assign some values to that slice:

object[0] = []byte("hello")
object[1] = []byte("world")
Enter fullscreen mode Exit fullscreen mode

Finally, we turn the whole thing into an empty interface:

test := makeInterface(object)
Enter fullscreen mode Exit fullscreen mode

Now, since we have a slice of interfaces, I struggled a bit trying to figure out why this does not work:

for _, item := range test 
    fmt.Println(item)
}

./test.go:46:23: cannot range over test (variable of type interface{})
Enter fullscreen mode Exit fullscreen mode

Type casting

Of course, it makes perfect sense. test is not a slice of empty interfaces, but an empty interface containing a slice of empty interfaces. In order to access those, we first need to cast the interface to a slice of interfaces:

testSlice := test.([]interface{})
Enter fullscreen mode Exit fullscreen mode

Then we can range over this slice and do additional typecasting:

testSlice := test.([]interface{})
     for _, item := range testSlice {
          byteSlice := item.([]byte)
          fmt.Println(string(byteSlice))
     }

hello
world
Enter fullscreen mode Exit fullscreen mode

Hi! 👋 I’m Tom. I’m a software engineer, a technical writer and IT burnout coach. If you want to get in touch, check out https://tomdeneire.github.io

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more