简体   繁体   中英

Obtaining reflect.Ptr type to field in a Go struct

I am trying to pass to a third-party package a variadic list of pointers to fields in a struct. The package accepts a variadic interface{} list ( func Persist(...interface) error ), where each of the interface values is a pointer to a variable. I created a function that mocks how the third-party library and prints out the Type and Kind of the pointers (called mockFunction below).

When I pass it the address of the struct variables in a non-variadic way, they have their primitive Types and Values within the mocked function using the reflect calls. However, when I pass them in a variadic way using expansion, they have Type: Type: reflect.Value and Kind: struct . The third-party package does not know how to handle them in this form.

I would like to figure out a way to call the third-party package with a slice of interface{} (eg inv := make([]interface{}, 3) and use variadic expansion on the call Persist(inv...) if at all possible.

Here is the code with a link to Go Playground below:

package main

import (
  "fmt"
  "reflect"
)

type Investment struct {
  Price  float64
  Symbol string
  Rating int64
}

func main() {
  inv := Investment{Price: 534.432, Symbol: "GBG", Rating: 4}
  s := reflect.ValueOf(&inv).Elem()
  variableParms := make([]interface{}, s.NumField())
  for i := 0; i < s.NumField(); i++ {
    variableParms[i] = s.Field(i).Addr()
  }

  // non-variadic call
  mockFunction(&inv.Price, &inv.Symbol, &inv.Rating)
  //variadic call
  mockFunction(variableParms...)
}

func mockFunction(values ...interface{}) {
  for i, value := range values {
    rv := reflect.ValueOf(value)
    fmt.Printf("value %d has Type: %s and Kind %s\n", i, rv.Type(), rv.Kind())
  }
}

Go Playground Link

When I run it with the non-variadic parameters, the call to mockFunction returns the native Types and Kinds and the third-party package processes them fine:

value 0 has Type: *float64 and Kind ptr
value 1 has Type: *string and Kind ptr
value 2 has Type: *int64 and Kind ptr

When I run it with the variadic parameters, the values are different and the third-party package does not know how to handle these types:

value 0 has Type: reflect.Value and Kind struct
value 1 has Type: reflect.Value and Kind struct
value 2 has Type: reflect.Value and Kind struct

Is there any way to structure the slice definition and the call to what is placed in to the slice so that it can be variadic expanded and look like passing the pointers to the struct fields in the non-variadic way?

Addr() returns the reflect Value for the field pointer. Call Ptr() on the value to get the actual pointer as an interface{}.

variableParms[i] = s.Field(i).Addr().Ptr()

playground

I think that perhaps Go's handling for this case has changed since 2014 - certainly the code above no longer works for me with Go 1.10...

However the following code works for me to create an appropriate []interface{} to use in the described way...

func settableSliceFromStruct(inStruct interface{}) ([]interface{}, error) {
    t := reflect.TypeOf(inStruct)
    if t.Kind() != reflect.Ptr {
        return nil, errors.New("can only assign values with pointer to struct")
    }
    v := reflect.ValueOf(inStruct).Elem()
    t = t.Elem()

    dataColumns := make([]interface{}, 0, t.NumField())
    for i := 0; i < t.NumField(); i++ {
        if weWantToIncludeThis(t.Field(i)) {
            dataColumns = append(dataColumns, v.Field(i).Addr().Interface())
        }
    }
    return dataColumns, nil
}

The critical part here would be for your code to use:

variableParms[i] = s.Field(i).Addr().Interface()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM