Reflections in Go

Aman Jain
3 min readApr 5, 2020

We all come around situations during programming, where we need to determine the types of variables or functions. Sometimes, there comes a use case when you don’t actually know the type of variable when the program was written(during compile time), but you need to determine it at runtime. Maybe, you want to map a network request into a variable or build a tool that works with different types. So, reflection could be a possible solution for these kinds of problems.

Reflection is the ability of a program to identify types of variables or functions at runtime. It’s a form of meta programming.

A particular use case of reflection, that is generally encountered while using go is unmarshalling JSON into a variable. We call the json.Unmarshal(a, b) function for the same. It takes in two parameters:

  • The first argument is of type []byte.
  • The second argument is of type interface{} i.e the one we want to populate.

If you dig deeper into the native encoding/json library of go, you get to a package-private method called unmarshal. The relevant part looks like this:

func (d *decodeState) unmarshal(v interface{}) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return &InvalidUnmarshalError{reflect.TypeOf(v)}
}
d.scan.reset()
d.scanWhile(scanSkipSpace)
// We decode rv not rv.Elem because the Unmarshaler interface
// test must be applied at the top level of the value.
err := d.value(rv)
if err != nil {
return d.addErrorContext(err)
}
return d.savedError
}

It’s using reflection to validate that v is the correct kind of variable, a pointer. If it is, then the reflected version of v, called rv, is passed to the value method.

reflect.TypeOf()

Reflection is provided by the reflect package. It provides two important types, Type and Value. reflect.Typeof() function accepts an interface{} and returns the dynamic type as the reflect.Type.

fmt.Println(reflect.TypeOf(2)) // 2

The TypeOf(2) function call assigns the value 2 to the interface{} parameter. The assignment from the concrete value to an interface type performs an implicit interface{} conversion which creates an interface value consisting of two components: it’s dynamic type is the operand’s type (int) and it’s dynamic value is the operand’s value(2).

interface value => dynamic operand's type + dynamic operand's value

Also, note that fmt.Printf() function internally uses reflect.TypeOf to determine type of the variable.

x := 2
fmt.Printf("%T\n", x) // int

reflect.ValueOf()

The other important type of reflect package is Value. The reflect.Valueof() function accepts an interface{} and returns a reflect.Value containing the interface’s dynamic value.

x := 2
fmt.Println(reflect.ValueOf(x)) // 2

Calling the Type method on a Value returns its type as a reflect.Type :

x := reflect.ValueOf(2) // a reflect.Value
fmt.Println(x) // 2
t := x.Type()
fmt.Println(t) // int
i := x.Interface() // an interface{}
v := x.(int)
fmt.Println(v) // 2

A reflect.Value and an interface{} can both hold arbitrary/random values. The difference is that an empty interface hides the representation and intrinsic operations of the value it holds and exposes none of its methods, so unless we know its dynamic type and use a type assertion to peer inside it (as we did above), there is little we can do to the value within. In contrast, a Value has many methods for inspecting its contents, regard less of its type.

One such intrinsic method used on value is Kind()

x := reflect.ValueOf(2)
fmt.Println(x.Kind()) // int

Difference between Kind and Type of reflection package

Let us understand the difference with the help of an example which is illustrated below:

package main

import (
"fmt"
"reflect"
)

type Transactions struct {
TransactionID string `json:"txn_id,omitempty"`
Amount float64 `json:"amount,omitempty"`
}

func printTypes(i interface{}) {
type := reflect.TypeOf(i)
kind := t.Kind()
fmt.Println("Type ", type)
fmt.Println("Kind ", kind)
}
func main() {
structForTxn := Transactions{
TransactionID: "abc-1231-cd",
Amount: 100,
}
printTypes(structForTxn)
}

The output for above program will be:

Type  main.Transactions
Kind struct

I think you will now be clear about the differences between the two. Type represents the actual type of the interface{}, in this case main.Transactions and Kind represents the specific kind of the type. In this case, it's a struct.

That’s all folks. This was my first attempt at writing blogs on medium. I hope I was able to help someone.

References: https://www.gopl.io/

--

--