GOLang: Reflection

Saurabh Sharma

Must Read

Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types.

Reflection is a way to examine own structure. Let’s try and learn it via examples.

func main() {
	//exampleOne()
	i := 0
	fmt.Println(reflect.TypeOf(i))
	fmt.Println(reflect.ValueOf(i))
}

Here I have defined a variable i := 0 and inspecting it using reflection.

int
0

TypeOf returns the reflection Type that represents the dynamic type of i.

i := 0
fmt.Println(" Reflection: Int")
fmt.Println(reflect.TypeOf(i))
fmt.Println(reflect.ValueOf(i))
str := "Abc"
fmt.Println(reflect.TypeOf(str))
fmt.Println(reflect.ValueOf(str))

The same way it works on other type

int
0
string
Abc

How about using a custom type instead of int

	type MyInt int
	var myInt MyInt
	
	myInt = 0
	fmt.Println(reflect.TypeOf(myInt))
	fmt.Println(reflect.ValueOf(myInt))
main.MyInt
0

Variable i and myInt have distinct static types. Underlying types are same for both of them, but we cannot assign them to each other without conversion.

myInt = i

Any statement like that will lead to a compile time error.

cannot use i (type int) as type MyInt in assignment

i = 10
myInt = MyInt(i)
fmt.Println(reflect.TypeOf(myInt))
fmt.Println(reflect.ValueOf(myInt))
main.MyInt
10

Let’s define a custom type with more content and read about using the StructField

// MyStructTags Reflect pacakge to learn more about a field.
type MyStructTags struct {
	Fi int64   `json:"intField" defines:"age"`
	Fb bool    `json:"boolField" defines:"state"`
	Ff float64 `json:"floatField" defines:"currency"`
	Fs string  `json:"stringField" defines:"name"`
}

// ReadTags Reads the tag
func ReadTags(tt MyStructTags, ix int) {
	ttType := reflect.TypeOf(tt)
	ixField := ttType.Field(ix)                                                           // getting field at a position ix
	fmt.Printf("%v:%s:%v:%v\n", ixField.Tag, ixField.Name, ixField.Offset, ixField.Type) // printing tags
}
myRef := ref.MyStructTags{1, false, 1.52, "What is my name?"}

for i = 0; i < 4; i++ {
	ref.ReadTags(myRef, i)
}

The output is as under

json:"intField" defines:"age":Fi:0:int64
json:"boolField" defines:"state":Fb:8:bool
json:"floatField" defines:"currency":Ff:16:float64
json:"stringField" defines:"name":Fs:24:string

Looking at the documentation for the SructField

type StructField struct {
    // Name is the field name.
    Name string
    // PkgPath is the package path that qualifies a lower case (unexported)
    // field name. It is empty for upper case (exported) field names.
    // See https://golang.org/ref/spec#Uniqueness_of_identifiers
    PkgPath string

    Type      Type      // field type
    Tag       StructTag // field tag string
    Offset    uintptr   // offset within struct, in bytes
    Index     []int     // index sequence for Type.FieldByIndex
    Anonymous bool      // is an embedded field
}

Setting Values

How about setting the value of the field?

type MyInt int
var myInt MyInt

reflect.ValueOf(myInt).SetInt(22)

reflect: reflect.flag.mustBeAssignable using unaddressable value

Settability is a property of a reflection Value, and not all reflection Values have it. 

if reflect.ValueOf(myInt).CanSet() {
	reflect.ValueOf(myInt).SetInt(22)
}else {
	fmt.Println(" CanSet returned false")
}

We will use Elem to set the value

Elem returns the value that the interface v contains or that the pointer v points to. It panics if v’s Kind is not Interface or Ptr. It returns the zero Value if v is nil.

fmt.Println(reflect.ValueOf(&myInt).Kind())
fmt.Println(reflect.ValueOf(&myInt).Elem().CanSet())
reflect.ValueOf(&myInt).Elem().SetInt(24)
fmt.Println(reflect.ValueOf(myInt).Interface())
ptr
true
24

Leave a Reply

Your email address will not be published. Required fields are marked *