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 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
}
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
}
Then I make an object which is a slice of empty interfaces (following the example in the above):
object := make([]interface{}, 2)
Then I assign some values to that slice:
object[0] = []byte("hello")
object[1] = []byte("world")
Finally, we turn the whole thing into an empty interface:
test := makeInterface(object)
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{})
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{})
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
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)