Golang

Reflecting on structs

Reflection in Go allows  to manipulate objects and are most useful when dealing with structs.

I always wished I could range over a struct. But “range” only supports builtin types such as string, list, and map. Reflect makes it easy to do so.

I have a package that contains a function, I use frequently to do exactly this.  It takes in a struct and copies over the elements to a map so we can range over it.  https://github.com/mariadesouza/structutil

Here is a small example:

package main
import ( "fmt"
 "github.com/mariadesouza/structutil"
)
type testStruct struct { 
   Name       string 
   Email      []string 
   Occupation string
}
func main() {
  test := testStruct{"Ethan", []string{"emdesouza@gmail.com"}, "engineer"} 
  m := structutil.StructToMap(&test) 

  for key, val := range *m { 
    fmt.Println(key, ":", val)
  }
}

How it works?

Main concepts used:

type

Every variable in GO has a static type including elements of a struct.

interface{}

In go an interface{} represents an empty set of methods that satisfies all variable types

reflect

Go has a package called reflect that allows us to inspect the type and value stored in any data type.

I used reflect on a struct  and turn it into a map of names to values.  The call to ValueOf returns a Value representing the run-time data. This helps us to loop through the runtime elements

structmap := make(map[string]interface{}) // map to hold key - element
 elements := reflect.ValueOf(myStruct).Elem()  
 typeofT := elements.Type()    // type of Element
 for i := 0; i <elements.NumField(); i++ { 
     f := elements.Field(i) 
     elementName := typeofT.Field(i).Name //key name of element 
     valueOfElement := f.Interface() //returns current value as an interface{}
     structmap[elementName] = valueOfElement 
 } 

For this to work,  the struct elements have to be exported i.e. start with a capital or you will get a panic error as below.

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

https://play.golang.org/p/Kyn-k4rAFFX

Access private struct fields using reflection

We can read private struct variables in a package using reflection. However,  private field values cannot be changed.

E.g.

package main
import (
	"fmt"
	"reflect"
	"container/list"
)

func main() {
	cl := list.New()
        cl.PushFront(2)
	fieldvaluelen := reflect.ValueOf(cl).Elem().FieldByName("len")
	fmt.Println(fieldvaluelen.Int()) 
}

Output: 1

Reference

Laws of reflection

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s