What is settable (CanSet)
First of all, you need to make it clear that the settings are for reflect.Value. To convert ordinary variables into reflect.Value, you need to use reflect.ValueOf() to convert.
So why is there such a "settable" method? For example, the following example:
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println(v.CanSet()) // false
All function calls in golang are value replication, so when reflect.ValueOf is called, an x has been copied and passed in. The v obtained here is the value of a copy of x. So at this time, we want to know if I can set the x variable here through v. We need a method to help us do this: CanSet()
However, it is very obvious that since we are passing a copy of x, the value of x cannot be changed at all. It shows false here.
So what if we pass the address of x to it? The following example:
var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println(v.CanSet()) // false
We passed the address of the x variable to reflect.ValueOf. It should be CanSet. But here is one thing to pay attention to, here v points to the pointer of x. So the CanSet method judges whether the pointer of x can be set. The pointer cannot be set, so false is returned here.
So what we need to be able to judge by the value of this pointer is whether the element pointed to by this pointer can be set. Fortunately, reflect provides the Elem() method to get the "element pointed to by the pointer".
var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println(v.Elem().CanSet()) // true
Finally returns true. But there is a prerequisite when using this Elem(), the value here must be reflect.Value converted from pointer object. (Or reflect.Value converted by interface object). This premise is not difficult to understand, if it is an int type, how can it have a pointed element? So, pay attention to this when using Elem, because if this premise is not met, Elem will directly trigger panic.
After judging whether it can be set, we can use SetXX series methods to make the corresponding settings.
var x float64 = 3.4
v := reflect.ValueOf(&x)
if v.Elem().CanSet() {
v.Elem().SetFloat(7.1)
}
fmt.Println(x)
More complex types
For complex slice, map, struct, pointer and other methods, I wrote an example:
package main
import (
"fmt"
"reflect"
)
type Foo interface {
Name() string
}
type FooStruct struct {
A string
}
func (f FooStruct) Name() string {
return f.A
}
type FooPointer struct {
A string
}
func (f *FooPointer) Name() string {
return f.A
}
func main() {
{
// slice
a := []int{1, 2, 3}
val := reflect.ValueOf(&a)
val.Elem().SetLen(2)
val.Elem().Index(0).SetInt(4)
fmt.Println(a) // [4,2]
}
{
// map
a := map[int]string{
1: "foo1",
2: "foo2",
}
val := reflect.ValueOf(&a)
key3 := reflect.ValueOf(3)
val3 := reflect.ValueOf("foo3")
val.Elem().SetMapIndex(key3, val3)
fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3]
}
{
// map
a := map[int]string{
1: "foo1",
2: "foo2",
}
val := reflect.ValueOf(a)
key3 := reflect.ValueOf(3)
val3 := reflect.ValueOf("foo3")
val.SetMapIndex(key3, val3)
fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3]
}
{
// struct
a := FooStruct{}
val := reflect.ValueOf(&a)
val.Elem().FieldByName("A").SetString("foo2")
fmt.Println(a) // {foo2}
}
{
// pointer
a := &FooPointer{}
val := reflect.ValueOf(a)
val.Elem().FieldByName("A").SetString("foo2")
fmt.Println(a) //&{foo2}
}
}
If you can understand the above example, then you basically understand the CanSet method.
In particular, you can pay attention to the fact that map and pointer do not need to pass pointers to reflect.ValueOf when they are modified. Because they are pointers themselves.
So when calling reflect.ValueOf, we must be very clear in mind, the underlying structure of the variable we want to pass. For example, map actually passes a pointer, we don't need to pointer it anymore. And slice, what actually passes is a SliceHeader structure. When we modify Slice, we must pass the pointer of SliceHeader. This point often requires our attention.
CanAddr
As you can see in the reflect package, in addition to CanSet, there is also a CanAddr method. What is the difference between the two?
The difference between the CanAddr method and the CanSet method is that for some private fields in the structure, we can get its address, but we cannot set it.
For example, the following example:
package main
import (
"fmt"
"reflect"
)
type FooStruct struct {
A string
b int
}
func main() {
{
// struct
a := FooStruct{}
val := reflect.ValueOf(&a)
fmt.Println(val.Elem().FieldByName("b").CanSet()) // false
fmt.Println(val.Elem().FieldByName("b").CanAddr()) // true
}
}
Therefore, CanAddr is a necessary and insufficient condition for CanSet. A Value if CanAddr is not necessarily CanSet. But if a variable is CanSet, it must be CanAddr.
Source code
Suppose we want to implement the Value element CanSet or CanAddr, we will probably use the marker bit mark. This is indeed the case.
Let's first look at the structure of Value:
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag
}
It should be noted here that it is a nested structure with a flag nested, and this flag itself is a uintptr.
type flag uintptr
This flag is very important. It can not only express the type of the value, but also some meta information (such as whether it is addressable, etc.). Although flag is a uint type, it is represented by a bit mark.
First, it needs to represent the type. There are 27 types in golang:
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
So using 5 bits (2^5-1=63) is enough to put so many types. So the lower 5 bits of the flag are the structure type.
Sixth flagStickyRO: whether the flag is an internal structure Private property
seventh flagEmbedR0: whether the flag is an internal structure of a private property nested
eighth flagIndir: whether a flag value of ptr is a pointer to a stored
ninth flagAddr: this marker Is the value addressable? The
tenth bit flagMethod: mark that value is an anonymous function
One of the more difficult to understand is flagStickyRO, flagEmbedR0
Look at the following example:
type FooStruct struct {
A string
b int
}
type BarStruct struct {
FooStruct
}
{
b := BarStruct{}
val := reflect.ValueOf(&b)
c := val.Elem().FieldByName("b")
fmt.Println(c.CanAddr())
}
In this example, the flagEmbedR0 flag bit of c is 1.
So let’s go back to the CanSet and CanAddr methods
func (v Value) CanAddr() bool {
return v.flag&flagAddr != 0
}
func (v Value) CanSet() bool {
return v.flag&(flagAddr|flagRO) == flagAddr
}
Their method is to "AND" the value's flag and flagAddr or flagRO (flagStickyRO, flagEmbedR0).
The difference between them is whether to judge the two bits of flagRO. So their difference, in other words, is to "judge whether this Value is a private property", and the private property is read-only. Cannot be set.
application
In the process of developing the collection ( https://github.com/jianfengye/collection) library, I used such a method. I hope to design a methodfunc (arr *ObjPointCollection) ToObjs(objs interface{}) error
that can set the objs reflect.Value in ObjPointCollection to the parameter objs.
func (arr *ObjPointCollection) ToObjs(objs interface{}) error {
arr.mustNotBeBaseType()
objVal := reflect.ValueOf(objs)
if objVal.Elem().CanSet() {
objVal.Elem().Set(arr.objs)
return nil
}
return errors.New("element should be can set")
}
Instructions:
func TestObjPointCollection_ToObjs(t *testing.T) {
a1 := &Foo{A: "a1", B: 1}
a2 := &Foo{A: "a2", B: 2}
a3 := &Foo{A: "a3", B: 3}
bArr := []*Foo{}
objColl := NewObjPointCollection([]*Foo{a1, a2, a3})
err := objColl.ToObjs(&bArr)
if err != nil {
t.Fatal(err)
}
if len(bArr) != 3 {
t.Fatal("toObjs error len")
}
if bArr[1].A != "a2" {
t.Fatal("toObjs error copy")
}
}
to sum up
When CanAddr and CanSet first come into contact, there will be some confusion, or you need to understand the flag of reflect.Value to fully understand it.