Home > Enterprise >  get reflect.struct from interface
get reflect.struct from interface

Time:09-26

hi a have this func for get type of value, but i try this and never can get reflect.struct:

type Test struct {
    Code int   
    Name string
}
func main(){
    test := getTest()
    data, err := getBytes(slice...)
    sanitizedFile := bytes.Split(data, []byte("\r\n"))
    err = Unmarshal(sanitizedFile[0], &test)
}
func getTest() interface{} {
    return Test{}
}

With this code i don't can get the reflecet.struct from v params in Unmarshall func

func Unmarshal(data []byte, v interface{}) error {
    rv := reflect.ValueOf(v)

    if rv.Kind() == reflect.Ptr {
        rvElem := rv.Elem()
        
        switch rvElem.Kind() {
        case reflect.Struct:
           // implement me
        }
    }
    return ErrInvalid
}

I would like to know if I can somehow find out if an interface is of type struct or access the values ​​of that struct.

CodePudding user response:

Maybe what you need is type assertion?

t, ok := v.(myStruct)

https://tour.golang.org/methods/15

In any case this code prints "struct":

    type tt struct {}

    var x tt
    var z interface{}
    z = x
    v := reflect.ValueOf(z).Kind()
    fmt.Printf("%v\n", v)

And see this for setting the value of a struct field using reflection:

Using reflect, how do you set the value of a struct field?

CodePudding user response:

I think the real problem here is illustrated by this quote:

I would like to know if I can somehow find out if an interface is of type struct or access the values ​​of that struct.

An interface value isn't "of type struct". Never! An interface value can contain a value whose type is some structure, but it is not a value of that type. It just contains one. This is similar to the way that a box1 you get from Amazon can contain a corkscrew, but the box is not a corkscrew, ever.

Given a non-nil value of type interface I for some interface type I, you know that you have a value that implements the methods of I. Since {} is the empty set of methods, all types implement it, so given a (still non-nil) value of type interface{}, you have a value that implements no methods. That's not at all useful by itself: it means you can invoke no methods, which means you can't do anything method-like.

But just because you can't do anything method-y doesn't mean you can't do anything at all. Any interface value, regardless of the interface type, can have a type-assertion used on it:

iv := somethingThatReturnsAnInterface()
cv := iv.(struct S) // assert that iv contains a `struct S`

If iv does in fact contain a struct S value—if that's what's inside the box once you open it—then this type-assertion doesn't panic, and cv winds up with the concrete value of type struct S. If panic is undesirable, we can use the cv, ok := iv.(struct S) form, or a type switch. All of these—including the version that panics—work by checking the type of the value inside the interface.

What this—or, more precisely, the way the Go language is defined—tells us is that the interface "box" really holds two things:

  • a concrete type, and
  • a concrete value.

Well, that is, unless it holds a <nil, nil> pair, in which case iv == nil is true. Note that the iv == nil test actually tests both parts.

If Go had a syntax for this, we could write something like iv.type and iv.value to get at the two separate parts. But we can't do that. We have to use type assertions, type-switch, or reflect. So, going back to this:

I would like to know if I can somehow find out if an interface is of type struct

we can see that the question itself is just a little malformed. We don't want to know if an interface value has this type. We want to know if a non-nil interface's held value is of this type, as if we could inspect iv.type and iv.value directly.

If you have a limited set of possible types, you can use the type-switch construct, and enumerate all your allowed possiblities:

switch cv := iv.(type) {
case struct S:
    // work with cv, which is a struct S
case *struct S:
    // work with cv, which is a *struct S
// add more cases as appropriate
}

If you need more generality, instead of doing the above, we end up using the reflect package:

tv := reflect.TypeOf(iv)

or:

vv := reflect.ValueOf(iv)

The latter is actually the more useful form, since vv captures both the iv.type pseudo-field and the iv.value pseudo-field.

As mkopriva notes in a comment, test, in your sample code, has type interface{}, so &test has type *interface{}. In most cases this is not a good idea: you just want to pass the interface{} value directly.

To allow the called function to set the object to a new value, you will want to pass a pointer to the object as the interface value. You do not want to pass a pointer to the interface while having the interface hold the struct "in the box" as it were. You need a reflect.Value on which you can invoke Set(), and to get one, you will need to follow an elem on the reflect.Value that is a pointer to the struct (not one that is a pointer to the interface).

There's a more complete example here on the Go Playground.


1This is partly an allusion to "boxed values" in certain other programming languages (see What is boxing and unboxing and what are the trade offs?), but partly literal. Don't mistake Go's interfaces for Java's boxed values, though: they are not the same at all.

  •  Tags:  
  • go
  • Related