1

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

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Emixam23
  • 3,854
  • 8
  • 50
  • 107
  • Addr() already returns a pointer value. Calling Pointer() on it is wrong. Probably you should extract the actual pointer via Interface() instead of Pointer(). – Volker Dec 17 '17 at 20:59

1 Answers1

1

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:

  • The argument must be a pointer to a struct.
  • There's no need to call CanAddr on the fields. The check for pointer to struct covers this.
  • The CanSet() skips over unexported fields. You may want it to panic instead.
  • The function panics for errors in the caller. Consider returning an error instead.

playground example

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242