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

Top comments (0)