Struct value of pointer, array and slice

2019-06-10 02:29发布

I want to have a generic way which will always return the struct value no matter if it is provided as pointer, slice or array.

My approach towards this looks:

func main() {
    p := Person{}

    if value(p).Kind() != reflect.Struct {
        fmt.Printf("Error 1")
    }
    if value(&p).Kind() != reflect.Struct {
        fmt.Printf("Error 2")
    }   
    if value([]Person{p}).Kind() != reflect.Struct {
        fmt.Printf("Error 3")
    }
    if value(&[]Person{p}).Kind() != reflect.Struct {
        fmt.Printf("Error 4")
    }
}

func value(m interface{}) reflect.Value {
    v := reflect.ValueOf(m)

    switch v.Kind() {
    case reflect.Ptr:
        v = v.Elem()

        fallthrough
    case reflect.Slice, reflect.Array:
        v = v.Elem()
    }

    return v
}

Go Playground

As you can see the problem lays with in getting the struct out of a slice or array.

How do I need to extend the above function to get the struct value from with in an array or slice?

Update: What I want to do is turn []People into People.

标签: reflection go
2条回答
做自己的国王
2楼-- · 2019-06-10 03:19

I assume that what you mean by "get out of the slice or array" is that you want the first element (that is, the element at index 0)? If that's what you want, then you should use the reflect.Value.Index() method. For example:

func value(m interface{}) reflect.Value {
    v := reflect.ValueOf(m)

    switch v.Kind() {
    case reflect.Ptr:
        v = v.Elem()
        if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
            v = v.Index(0)
        }
    case reflect.Slice, reflect.Array:
        v = v.Index(0)
    default:
        break LOOP
    }

    return v
}

Go playground

Note that I also slightly modified the flow logic. You were falling through to the slice/array case from the pointer case. You probably intended for the case condition to be tested again (so it'd effectively say, "if this was a pointer, now check if the thing it pointed to was a slice or an array"), but that's not how fallthrough works. Now it checks the case explicitly.

查看更多
疯言疯语
3楼-- · 2019-06-10 03:22

If you just want the type even if the slice is nil, you can use something like this:

func value(m interface{}) reflect.Type {
    t := reflect.Indirect(reflect.ValueOf(m)).Type()
    if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
        t = t.Elem()
        if t.Kind() == reflect.Ptr {
            t = t.Elem()
        }
        return t

    }
    return t
}

About Type.Elem(), from http://golang.org/pkg/reflect/#Type:

// Elem returns a type's element type.

// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.

//edit updated the function to work on a slice of pointers as well.

查看更多
登录 后发表回答