Using reflection in Golang

Bogdan Mihai Timofte
METRO SYSTEMS Romania
5 min readMar 26, 2019
Photo by Kelly Sikkema on Unsplash

In the context of programming, reflection means the ability of a program to examine and modify its own structure and behavior at runtime. The main purpose of reflection is to offer programmers the possibility to create generic pieces of software. Furthermore, reflection is the key for metaprogramming — the ability of a program to use another program as its input data.

Each programming language has its own reflection model, but each reflection model is built over the types system. So, before we use reflection, it is very important to have the best understanding of the types system of the programming language we work with (in this context, Go).

As Go is a statically typed programming language, this means that each variable has a unique, known and fixed type when you compile it.
Combined with the idea that we, as programmers, define each and every variable involved in our programs, we create to ourselves the premature opinion that reflection is pointless. Well…, this is not actually true each time.

Sometimes we want (or need) to work with variables or pieces of information that are not known at the moment when the program was written. This is applicable when you write a piece of software that handles data received over the network or a program that is able to work with different types. In these cases you need reflection.

reflect package

In the context of Go, if you want to use reflection you need to use the reflect package. This package implements runtime reflection in Go. It helps us to inspect types, kinds, and values for variables we are working on. Also, it implements a set of methods that are able to create new instances or alter the state of the existing ones.

Short disclaimer: The way how the reflect package is implemented assumes that you really know what you are doing when using it, so many methods and functions will panic if are not well used.

Type, Kind, Value

These three things are key to the reflection in Go. By using the standard reflect package mentioned above you can find each of these three pieces of information.

First thing I want to mention here is the difference between Type and Kind. Let’s take this example:

rType will contain the type of the variable r. In our example, the value will be main.Rectangle which is a (the type we defined) reflect.Type on abstraction layer and the Kind will be struct. rValue will be the actual value of the variable r.

Go is a statically typed programming language so rType, rKind, and rValue must have their own types. So far we can identify connections:

  • reflect.TypeOf takes an interface as a parameter and returns a reflect.Type
  • reflect.ValueOf takes an interface as a parameter and returns a reflect.Value
  • Kind is a method that returns a reflect.Kind

Although, there is a major difference between reflect.Value and the actual value of the variable. So, reflect.Value can only be used in reflection context. To convert from reflect.Value to an actual general usable value we need to use the method reflect.Value.Interface and cast the resulting interface to the type you desire.

Inspecting variables

When we put on observation a variable which has struct Kind, reflection offers you details about the number of fields in the struct and each field structure. These pieces of information can be found in reflect.StructField. Here you can find info about the name of a field, its type and kind or list of tags.

If the variable is a slice, channel, pointer, map or array you can extract the type of the variable and after that using vType.Elem() you can find out the type of the containing elements.

Run in the playground to check the output.

Altering states or creating new instances

Inspecting variables is cool and could be a very powerful tool, but another use for reflection is to alter the state of existing variables or even to create new instances at runtime. The reflect package has implemented functions that are able to read, set and create values. Don’t forget we work in a reflection context, so we need to convert the value of the variable into reflect.Value.

To modify the value of a variable using reflection you will need to get a pointer to the value. Getting the pointer is done by using addr := reflect.ValueOf(&var). To set a new value to the variable var you will use addr.Elem().Set(newValue), where newValue must be a reflect.Value.

To create a new instance you will use reflect.New(type) where type must be a reflect.Type. reflect.New will return a pointer to a value that can be modified by using Elem().Set() as described in the previous paragraph.

Go does not have generics, and getting back from a reflect.Value to a general use variable is a little bit counterintuitive because the original type of the variable is lost. So, to go back you need to use the Interface() method which will return a variable with type interface{} and to go back to the original type will need to cast the interface to the original type. See the example below for more details or run the example in the playground.

Reflection & Functions

Reflection offers the possibility to work also with functions. By using reflect.MakeFunc you are able to create new functions at runtime. This function requires a reflect.Type for the function and closure with 2 []reflect.Value as input & output parameters.

Run the example in the playground.

Instead of conclusion

This is just a small introduction to how powerful reflection can be and the main purpose of this article is to give you an idea about what can be made by using it. Reflection is a very expressive and powerful tool but it is essential to use it with great responsibility.

The reflection-based code can be fragile. The program passed the compilation step without errors, but at runtime, unexpected errors can appear and the whole program will panic.

It is necessary to speak about downsides. In my opinion, a big downside is that reflection may reduce the safety and accuracy of refactoring and analysis tools because they can’t determine and rely on type information.

So, long story short, with great power comes great responsibility. Pay lots of attention when using reflection and enjoy its flexibility!

--

--