Reputation: 11502
I have some question about Go reflect behaviour.
I have a slice named src
defined in []interface{}
type. I'd like to get the actual type of each element within the slice. This is what I did:
src := []interface{}{"noval", 0, nil}
srcType := reflect.ValueOf(src)
for i := 0; i < srcType.Len(); i++ {
each := srcType.Index(i)
if each.Interface() == nil {
fmt.Println("value", each.Interface(), "is nil")
} else {
switch each.Kind() {
case reflect.String:
fmt.Println("value", each.Interface(), "is string")
case reflect.Int, reflect.Int16:
fmt.Println("value", each.Interface(), "is int")
default:
fmt.Println("value", each.Interface(), "is ?")
}
}
}
Output:
value noval is ?
value 0 is ?
value <nil> is nil
For some reason, the data type of element "noval"
is not detected as string
, hence the default
block is executed within the switch
.
Also the 0
value should be identified as reflect.Int
but the default
block is called again.
Can somebody enlighten me please, thank you in advance.
Upvotes: 1
Views: 2000
Reputation: 417662
src
is a slice whose element type is interface{}
. So each element you obtain is of static type interface{}
, so their "kind" will be reflect.Interface
. Adding a new reflect.Interface
case will reveal that:
case reflect.Interface:
fmt.Println("value", each.Interface(), "is interface")
Output will be (try it on the Go Playground):
value noval is interface
value 0 is interface
value <nil> is nil
If you want the "wrapped" element in the interface, use Value.Elem()
:
} else if each.Kind() == reflect.Interface {
switch each.Elem().Kind() {
case reflect.String:
fmt.Println("value", each.Interface(), "is string")
case reflect.Int, reflect.Int16:
fmt.Println("value", each.Interface(), "is int")
default:
fmt.Println("value", each.Interface(), "is ?")
}
}
Then output will be (try it on the Go Playground):
value noval is string
value 0 is int
value <nil> is nil
Also note that you're "switching" over the kind of the values, not over their actual types. What this means is that values of multiple types may end up in specific cases, like in this one:
type mystr string
src := []interface{}{"noval", 0, nil, mystr("my")}
srcType := reflect.ValueOf(src)
// ...
Output of this will be (try it on the Go Playground):
value noval is string
value 0 is int
value <nil> is nil
value my is string
The value mystr("my")
is detected as string
, because it is of string
"kind", but its type is not string
but mystr
. This may or may not be what you want. If you want to distinguish between a value of type string
and mystr
, then you should "switch" over the actual type of values like in this example:
} else if each.Kind() == reflect.Interface {
switch each.Elem().Type() {
case reflect.TypeOf(""):
fmt.Println("value", each.Interface(), "is string")
case reflect.TypeOf(0):
fmt.Println("value", each.Interface(), "is int")
case reflect.TypeOf(mystr("")):
fmt.Println("value", each.Interface(), "is mystr")
default:
fmt.Println("value", each.Interface(), "is ?")
}
}
Then output will be (try it on the Go Playground):
value noval is string
value 0 is int
value <nil> is nil
value my is mystr
As you can see "nova"
is detected as a value of type string
, and mystr("my")
is properly detected as a value of type mystr
.
Also note that for what you're trying to do, you don't need reflection, just use a type switch:
src := []interface{}{"noval", 0, nil}
for _, v := range src {
switch v.(type) {
case string:
fmt.Println("value", v, "is string")
case int:
fmt.Println("value", v, "is int")
case nil:
fmt.Println("value", v, "is nil")
default:
fmt.Println("value", v, "is ?")
}
}
Output (try it on the Go Playground):
value noval is string
value 0 is int
value <nil> is nil
Upvotes: 4