I just can't find the way to get a slice of pointer to each attribute of a given struct. I am using reflection to get my pointers (Thanks to https://stackoverflow.com/a/24348352/6093604)
if valueField.CanAddr() {
address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
}
As you can see, valueField.Addr().Pointer() returns a pointer addr value, however, using reflection, I would like to get a usable pointer for sql.Rows.Scan()
So what did I do is:
func StructAttributesToPointersSlice(object interface{}) []interface{} {
val := reflect.ValueOf(object)
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()
}
var ptrs []interface{}
for i := 0; i < val.NumField(); i++ {
valueField := val.Field(i)
if valueField.Kind() == reflect.Ptr {
valueField = valueField.Elem()
}
if valueField.CanAddr() {
ptrs = append(ptrs, valueField.Addr().Pointer())
}
}
return ptrs
}
But when I try to use it for Scan()
sql function:
var values []interface{}
// Iterate over each rows got from the query
for rows.Next() {
ptrs := utils.StructAttributesToPointersSlice(&newObject)
for _, item := range ptrs {
fmt.Println(reflect.TypeOf(item))
}
err = rows.Scan(ptrs...)
if err != nil {
return nil, model.Status{Code: http.StatusInternalServerError, Error: err.Error()}
} else {
values = append(values, newObject)
}
}
I am getting this error:
sql: Scan error on column index 0: destination not a pointer
I know it's because it's not the good type since it's a uintptr
, but then how to transform it into usable pointer?
Thanks
Use unsafe.Pointer to convert a uintptr to a pointer of some type. As an example, the following expression converts uintptr u
to a T
pointer:
(*T)(unsafe.Pointer(u))
This conversion does not help in StructAttributesToPointersSlice
because the struct fields can be of any type. Also, the conversion from uintptr
is not needed and unsafe.
The expression valueField.Addr()
is the reflect.Value for the pointer to the field. Call Interface()
to get the actual pointer. To fix the program, change
ptrs = append(ptrs, valueField.Addr().Pointer())
to
ptrs = append(ptrs, valueField.Addr().Interface())
Here's a simplified version of the function:
func StructAttributesToPointersSlice(object interface{}) []interface{} {
v := reflect.ValueOf(object)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic("argument must be a pointer to struct")
}
v = v.Elem()
var result []interface{}
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if !f.CanSet() {
continue
}
result = append(result, f.Addr().Interface())
}
return result
}
Some notes about this code: