Get a pointer to a value using reflection

I have a function that iterates through all the interface fields passed as a parameter. For this, I use reflection. The problem is that I do not know how to get the address of a field without a pointer. Here is an example:

type Z struct { Id int } type V struct { Id int FZ } type T struct { Id int FV } 

The above code represents my test structures. Now here is the actual function that traverses the specified structure and lists the details about it:

 func InspectStruct(o interface{}) { val := reflect.ValueOf(o) if val.Kind() == reflect.Interface && !val.IsNil() { elm := val.Elem() if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { val = elm } } if val.Kind() == reflect.Ptr { val = val.Elem() } for i := 0; i < val.NumField(); i++ { valueField := val.Field(i) typeField := val.Type().Field(i) address := "not-addressable" if valueField.Kind() == reflect.Interface && !valueField.IsNil() { elm := valueField.Elem() if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { valueField = elm } } if valueField.Kind() == reflect.Ptr { valueField = valueField.Elem() } if valueField.CanAddr() { address = fmt.Sprint(valueField.Addr().Pointer()) } fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name, valueField.Interface(), address, typeField.Type, valueField.Kind()) if valueField.Kind() == reflect.Struct { InspectStruct(valueField.Interface()) } } } 

And here is the actual test after creating / initializing the structure:

 t := new(T) t.Id = 1 tF = *new(V) tFId = 2 tFF = *new(Z) tFFId = 3 InspectStruct(t) 

And finally, the output of the InspectStruct call:

 Field Name: Id, Field Value: 1, Address: 408125440 , Field type: int , Field kind: int Field Name: F, Field Value: {2 {3}}, Address: 408125444 , Field type: main.V , Field kind: struct Field Name: Id, Field Value: 2, Address: not-addressable , Field type: int , Field kind: int Field Name: F, Field Value: {3}, Address: not-addressable , Field type: main.Z , Field kind: struct Field Name: Id, Field Value: 3, Address: not-addressable , Field type: int , Field kind: int 

As you can see, I use recursion, so if one of the fields is a view of the structure, I call InspectStruct for it. My problem is that although all the fields were initialized for the entire hierarchy β€œt” of the whole structure, I can’t get the address for any field located at a deeper depth than β€œt”. I would really appreciate any help.

+9
source share
4 answers

Passing reflect.Value instead of interface{} seems to reflect.Value problem, however I don't know why valueField.Interface() not working.

Working example: http://play.golang.org/p/nleA2YWMj8

 func InspectStructV(val reflect.Value) { if val.Kind() == reflect.Interface && !val.IsNil() { elm := val.Elem() if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { val = elm } } if val.Kind() == reflect.Ptr { val = val.Elem() } for i := 0; i < val.NumField(); i++ { valueField := val.Field(i) typeField := val.Type().Field(i) address := "not-addressable" if valueField.Kind() == reflect.Interface && !valueField.IsNil() { elm := valueField.Elem() if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { valueField = elm } } if valueField.Kind() == reflect.Ptr { valueField = valueField.Elem() } if valueField.CanAddr() { address = fmt.Sprintf("0x%X", valueField.Addr().Pointer()) } fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name, valueField.Interface(), address, typeField.Type, valueField.Kind()) if valueField.Kind() == reflect.Struct { InspectStructV(valueField) } } } func InspectStruct(v interface{}) { InspectStructV(reflect.ValueOf(v)) } 
+10
source

Interface() does not work because the interface shell is returned. To give an idea of ​​what is going on, let's take a look at what we do without hesitation:

 type MyStruct struct { F Foo } type Foo struct { i int } func ExtractField(ptr *MyStruct) interface{} { return ptr.F } func main() { ms := &MyStruct{Foo{5}} f := ExtractField(ms).(Foo) // extract value fi = 19 fmt.Println(f, ms.F) // ??? fmt.Println(&f == &ms.F) // Not the same! } 

( Playground )

However, think about interface{} coming back. What is the packaging? The value of ptr.F is its copy . This is what value.Interface does, it returns an interface{} wrapping field. No more pointer metadata; they are completely separate from the original structure.

As you will notice, passing the value directly to reflect.ValueOf will always return false for CanAddr for the "top level" - because this address does not make sense, since it will give you the address of the copy of the value, changing it would mean nothing. (Keep in mind that pointers are also values ​​- if you need a field address with a pointer value, for example *Foo , you're really looking for **Foo ).

Thus, in our example above, if we naively passed reflect.ValueOf(ExtractField(ms)) we would get ValueOf f , which not only does not have a ValueOf address, but does not even address according to the reflection, because it never will give a valid address in terms of reflection (the only address it can give you is the address of the internal copy of the Value structure of the Value ).

So why does rabbit hole Value transfer work? Well, the only real way to say this is reflect.Value supports the necessary metadata when you use Elem and Field , but interface{} cannot. So while reflect.Value might look like this:

 // Disclaimer: not the real structure of a reflect.Value type Value struct { fieldAddress uintptr value Foo } 

All he can give you is

 // Again, an abstraction of the real interface wrapper // just for illustration purposes type interface{} struct { value Foo } 
+10
source

Today I went down to the reflecting rabbit hole, learned a lot by studying this code, and Jsor's answer, thanks. I came to a different conclusion, although about your problem, which led to a possibly more direct solution:

1) You pass a pointer to the structure when you originally called the function, but ...

2) When you recurs by calling 'InspectStruct (valueField.Interface ())', instead of passing inline structure by pointer, you pass it by value.

As you pass by value, go will create a temporary one and will not allow you to accept the address. Instead, when you recurs, call valueField.Addr (). An interface () that will pass a pointer to an inline structure.

  if valueField.Kind() == reflect.Struct { - InspectStruct(valueField.Interface()) + InspectStruct(valueField.Addr().Interface()) } 

With this change, I get the output you expect:

 Field Name: Id, Field Value: 1, Address: 842350527552 , Field type: int , Field kind: int Field Name: F, Field Value: {2 {3}}, Address: 842350527560 , Field type: lib.V , Field kind: struct Field Name: Id, Field Value: 2, Address: 842350527560 , Field type: int , Field kind: int Field Name: F, Field Value: {3}, Address: 842350527568 , Field type: lib.Z , Field kind: struct Field Name: Id, Field Value: 3, Address: 842350527568 , Field type: int , Field kind: int 
+2
source

The answer to @OneofOne's question is perfect, but it's better to add another check

 if valueField.IsValid() { fmt.Printf("Field Name: %s, Field Value: %v, Address: %v, Field type: %v, Field kind: %v\n", typeField.Name, valueField.Interface(), address, typeField.Type, valueField.Kind()) } 

this is necessary because sometimes you can request an interface from a structure with a null value. It happens, it will panic.

+1
source

Source: https://habr.com/ru/post/971162/


All Articles