Wrapping up Fundamentals of Go Interface: Representation, Reflection and More
Interface is, in many’s opinion, the shiniest part of the design of Go, and it adds flexibility to this statically typed language. As a noob gopher, I feel that leveraging interfaces does help deliver elegant and maintainable code. However, when not fully understood, interface might also be a great source of confusion. In this post, we will target some fundamental yet a bit complex part of Go interface, namely, its representation, its metaprogramming in reflection, and the role it plays in the new proposal of Go generics.
Representation of Interface
First of all, it’s very helpful and necesarry to understand how interface variables are stored. Simply put, an interface variable consists of two pointers with each of them stored in a word. One of these points to the concrete data that the interface variable holds, and the other points to a structure that stores the type information. In a trivial example. let’s suppose we have a type MyInt representing 32-bit integers:
type MyInt int32
and it implements a String() method:
func (m MyInt) String() string {
return fmt.Sprintf("MyInt [%d]", int32(m))
}
Now if we create a variable i of interface Stringer (which needs to have String() method), both words of i get set. That is, by doing:
myInt := MyInt(100)
var i Stringer
i = myInt
We essentially generate a structure like this:

The Type field is a type pointer that points to type metadata. Now when we check the type of this variable, we are actually using i.table->Type under the hood. When we call a function using the interface variable, the pointer that points to the actual MyInt data will be passed to the function pointers arranged (String() in this example) in interface table (essentially (*MyInt).String()).
Now that we know how interface variables are stored, we want to take a further look at when and how this 2-word pair (more specifically, the interface table) is generated. In general, the compiler will create a description structure for each concrete type, and within this structure there is a method list. Each interface type also has a method list in the description structure for interfaces. At compile time, the compiler will check if the mapping from certain concrete type to certain interface type is valid by checking the method lists. Then, at runtime, the actual interface table is generated by (again) looking for each method listed in interface type’s method list in concrete type’s method list, but this time associated with the actual pointers and data. Let’s say a concrete type and an interface type have m and n methods in their method lists respectively, then by sorting the two lists and traverse them in “two pointers” manner, the computation of interface table will take O(m + n + mlog(m) + nlog(n)) time, which is approximately O(m + n).
Interface and Reflection
Interface is very important in Go’s reflection, because some of the most useful functions in reflect package take in arguments of empty interface type. Having gone through the representation of interface variables, this is not difficult to understand – internally interface values separate the type and value information, making it a natural fit for metaprogramming. In this section we will take a detailed look at how interface value and reflection object are connected.
There are two basic structs defined in reflect package – Type and Value. In a simplified point of view we can view them as the two words introduced in the representation of an interface value. To convert an interface value to Type or Value, we can simply can reflect.TypeOf() and reflect.ValueOf(). Both struct provide plenty of methods to investigate and manipulate them. Among them, the most commonly used ones are:
Valuetype has aType()method that can retrieve theTypefrom the valueTypetype has aKind()method that can retrieve the underlying primitive type (returned as const uint) that the interface value stores
The conversion from reflection object back to interface values is also pretty useful. To do this we can use reflect.Value.Interface() method, which return the current value of Value variable as an empty interface.
func main() {
myInt := MyInt(100)
v := reflect.ValueOf(myInt)
t := v.Type()
kind := t.Kind()
fmt.Println(v, t, k) // 100 main.MyInt int32
val := v.Interface()
fmt.Println(val) // 100
}
Note that we don’t need cast the retrieved interface val back to MyInt because the empty interface returned from Interface() already has the type information associated with it.
Now that we’ve covered the basic two-way conversion between interface value and reflect object, we’ll get our hands on a more subtle aspect of reflection – settability. The principal goes simple: to set the underlying value of a Value object, the value must be addressable. Technically this is not hard to understand: when we call reflect.ValueOf() we essentially copy the variable we want to investigate into an empty interface, and then pass the copied data into the method, so we need the value to be addressable in order to manipulate the value itself (instead of the copy). To do this we need to make a Value object using the reference to the value.
var x float64 = 1.0
v := reflect.ValueOf(&x)
Now comes the subtle part. If we check the settability of v using v.CanSet(), we still get a false. This is because by passing &x to reflect.ValueOf(), we actually made x (instead of &x) addressable. In other words, we can now set the Value representation of x instead of &x. With this in mind, we can first dereference &x using reflect.Value.Elem() and set the dereference value.
underlyingV := v.Elem()
v.SetFloat64(2.0)
fmt.Println(v) // 2
When combined with other reflect methods, understanding the settability of reflect object can be more useful when dealing with structs. For example:
type T struct {
A int
B string
}
t := T{1, "Hello"}
v := reflect.ValueOf(&t)
elem := v.Elem()
for i := 0; i < elem.NumField(); i++ {
field := elem.Field(i)
switch field.Type().Kind() {
case reflect.Int:
field.SetInt(2)
case reflect.String:
field.SetString("Hi")
}
}
fmt.Println(t) // {2, Hi}
Interface and Go Generics
As Go’s new generics design draft gets released, seems like the language is eventually going to have this exciting missing piece. As you might expect, interface plays an important role in this new design. Simply put, the new design allows an extra “type parameter” to be specified before the regular parameters in a method, and also a “constraint” to be specified after the type parameter. This “constaint” is achieved using interfaces. As an example, let’s take a look at a simple method that takes in a slice of generic arguments and converts it to a slice of strings.
type Stringer interface {
String() string
}
func Stringify(type T Stringer)(s []T) []string {
ret := make([]string, 0)
for _, v := range s {
ret = append(ret, v.String())
}
return ret
}
The design also introduces a special type of interface – type list. It allows explicit types to be listed as constraints. This form of interface can no longer be used as ordinary interface type. For example:
type SignedInteger interface {
type int, int8, int16, int32, int64
}