1. Reflection goes from interface value to reflection object.
At the basic level, reflection is just a mechanism to examine the type and value pair stored inside an interface variable.
there are two types we need to know about in ==package reflect==: Type and Value.
Those two types give access to the contents of an interface variable
2. Reflection goes from reflection object to interface value.
Like physical reflection, reflection in Go generates its own inverse;
Given a reflect.Value we can recover an interface value using the Interface method;
- in effect the method packs the type and value information back into an interface representation and returns the result:
3. To modify a reflection object, the value must be settable.
Settability is a property of a reflection Value, and not all reflection Values have it.
The CanSet method of Value reports the settability of a Value; in our case,
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())prints:
settability of v: falseIt is an error to call a Setmethod on a non-settable Value. But what is settability?
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. When we say
var x float64 = 3.4
v := reflect.ValueOf(x)we pass a copy of x to reflect.ValueOf, so the interface value created as the argument to reflect.ValueOf is a copy of x, not x itself.
Thus, if the statement v.SetFloat(7.1) were allowed to succeed, it would not update x, even though v looks like it was created from x. Instead, it would update the copy of x stored inside the reflection value and x itself would be unaffected.
That would be confusing and useless, so it is illegal, and settability is the property used to avoid this issue.
If this seems bizarre, it’s not. It’s actually a familiar situation in unusual garb. Think of passing x
to a function: f(x)
- We would not expect
fto be able to modifyxbecause we passed a copy ofx’s value, notxitself.
If we want f to modify x directly we must pass our function the address of x (that is, a pointer to x): f(&x)
This is straightforward and familiar, and reflection works the same way. If we want to modify x
by reflection, we must give the reflection library a pointer to the value we want to modify.If we want to modify x by reflection, we must give the reflection library a pointer to the value we want to modify:
var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())The output so far is
type of p: *float64
settability of p: falseThe reflection object pisn’t settable, but it’s not pwe want to set, it’s (in effect) *p. To get to what ppoints to, we call the Elemmethod of Value, which indirects through the pointer, and save the result in a reflection Valuecalled v:
v := p.Elem()
fmt.Println("settability of v:", v.CanSet())Now v is a settable reflection object, as the output demonstrates,
settability of v: truehttps://go.dev/blog/laws-of-reflection