Laws of Reflection
Reflection in computing is the ability of a program to examine its own structure, particularly through types; it's a form of metaprogramming.
Interface
Reflection canot be explained without a good grasp on what is an interface in Go. I am not going to show an example of how to use interface because this should be quite trivial. A variable of interface type stores two key pieces of information.
Concrete value that is assigned to the variable.
Concrete type of the value.
We can extract the value and type of an interface variable using reflect.ValueOf
adn reflection.TypeOf
.
For example,
First Law
Reflection goes from interface value to reflection object.
Reflection Object(s)
There are two types of reflection objects, reflect.Value
and reflect.Type
.
reflect.Value
- Representation of runtime datareflect.Type
- Representation of a Go type
Kind
Reflection objects have a method called Kind()
which allows us to examine what sort of items is stored in the variable.
Here's a list of possible Kind
constants.
We can easily check if a variable is a pointer, slice, string or anything by equal comparison. Also it is not necessary to convert our variable into interface{}
type before we do reflection because the method reflect.ValueOf
and reflect.TypeOf
will do the work for us when we pass in the argument.
For example,
Elem
If we have a pointer value, we may want to extract the actual value of the pointer. We can use Elem
in this case.
For example,
However, be aware that Elem()
of a non-pointer will cause panic.
Explicit Value
We can grab the explicit value of a reflect.Value
by using the following list of methods.
String()
Slice()
Uint()
Int()
Float()
Pointer()
Complex()
Bool()
The list goes on, there may be other data types I am not listing here.
For example,
NumFields
If we have a reflect.Type
that is describing the concrete type of a struct variable, we can even examine the number of fields this struct has.
Second Law
Reflection goes from reflection object to interface value.
Given a reflect.Value
, we can recover the interface using Interface()
. The method packs the value and type information back into an interface representation and returns the result.
For example,
In other words, Interface()
is the inverse of ValueOf()
, except that the returned value is always of an empty interface type. We will need to perform type assertion to restore the original type. if we intend to use the variable in subsequeuent logic.
Third Law
To modify a reflection object, the value must be settable.
Settability is a bit like addressability, but stricter. It's the property that a reflection object can modify the actual storage that was used to create the reflection object. Settability is determined by whether the reflection object holds the original item.
Let's look at an example.
The code above will result in,
The variable x
is unsettable.
It is because we are passing a copy of x into ValueOf. Thus, if the set statement were allowed to succeed, it would not update x even though v looks like it was created from x. The Golang creator decided that this would be confusing and declared it as illegal.
The best way to think about this is to think of us passing x
into a function.
If we want it to be settable, we must pass in a pointer or a reference. However, the following does not work.
Pointer to an int
is still an integer. We need to get the element of the pointer to perform a successful set operation. This is because we are not trying to set pointer to some value. We are trying to set the value of the pointer to some value.
We can now extend the example to a struct.
The Contact
struct will become,
Last updated