How can I get the underlying value of a field having the fields name as a string?
I understand I need to use reflection but if I do will I have to continue using it throughout my code? Is there any way to assert?
I would just like to get the value of the field, the underlying struct, in this case a []Dice.
http://play.golang.org/p/KYOH8C7TAl
type Dice struct {
In int
}
type SliceNDice struct {
Unknown []Dice
}
func main() {
structure := SliceNDice{make([]Dice, 10)}
refValue := reflect.ValueOf(&structure).Elem().FieldByName(string("Unknown"))
slice := refValue.Slice(0, refValue.Len())
// cannot range over slice (type reflect.Value)
//for i,v := range slice {
// fmt.Printf("%v %v\n", i, v.In)
//}
for i := 0; i < slice.Len(); i++ {
v := slice.Index(i)
// v.In undefined (type reflect.Value has no field or method In)
fmt.Printf("%v %v\n", i, v.In)
}
}
If you know that the "Unknown" field is of type []Dice, you can use Value.Interface to get the underlying value and convert it with a type assertion:
slice := refValue.Interface().([]Dice)
for i,v := range slice {
fmt.Printf("%v %v\n", i, v.In)
}
http://play.golang.org/p/2lV106b6dH
Related
I have the following:
type Int32A int32
type Int32B int32
and would like to implement a function that can accept any slice of types whose kind is reflect.Int32 and convert it into []int32. For example:
func ConvertTypeSliceToInt32Slice(es «es-type») []int32 {
result := make([]int32, len(es))
for i := 0; i < len(result); i++ {
result[i] = es[i].(int32)
}
return result
}
func caller() {
Int32as := Int32A{1, 2}
Int32bs := Int32B{3, 5}
int32as := ConvertTypeSliceToInt32Slice(Int32as)
int32bs := ConvertTypeSliceToInt32Slice(Int32bs)
}
How can this be done far any arbitrary type definition whose kind is reflect.Int32? (Context: this function will be used to convert slices of proto enums; ie the full set of types is unknown and unbounded so performing a switch on each type isn't feasible).
Also, I'm using 1.17 so I can't use parameterized types (aka templates).
One attempt that doesn't work (it panics at is.([]interface{})):
func ConvertTypeSliceToInt32Slice(is interface{}) []int32 {
es := is.([]interface{})
result := make([]int32, len(es))
for i := 0; i < len(result); i++ {
result[i] = es[i].(int32)
}
return result
}
int32 in this case is an "underlying type", and ~ in a type parameter declaration is how you specify a constraint to an underlying type.
For example: https://go.dev/play/p/8-WAu9KlXl5
func ConvertTypeSliceToInt32Slice[T ~int32](es []T) []int32 {
result := make([]int32, len(es))
for i := 0; i < len(result); i++ {
result[i] = int32(es[i])
}
return result
}
If you need to use reflection, you can convert the type of each element before appending to the final slice:
func ConvertTypeSliceToInt32Slice(es interface{}) []int32 {
v := reflect.ValueOf(es)
int32ty := reflect.TypeOf(int32(0))
result := make([]int32, v.Len())
for i := 0; i < v.Len(); i++ {
result[i] = v.Index(i).Convert(int32ty).Interface().(int32)
}
return result
}
And if you need to ensure other convertible numeric types are not converted, you can compare the element type and panic or error as necessary:
if v.Type().Elem().Kind() != reflect.Int32 {
...
I write the code that fills data structures depending on its type. I need to call nested struct function if it exists.
Why I get zero value looking for function while the field is correct?
type (
SomeData struct {
Val NestedType
}
NestedType struct {
V1 string
}
)
func (t *NestedType) FillData(v int) {
t.V1 = fmt.Sprintf("Here is %v", v)
}
func main() {
i := SomeData{}
reflect.ValueOf(&i.Val).MethodByName("FillData").Call([]reflect.Value{reflect.ValueOf(555)})
fmt.Println(i) /// {{I hate 555}}
// BUT!
v := 666
newObj := reflect.New(reflect.TypeOf(SomeData{}))
fVal := newObj.Elem().FieldByName("Val")
fmt.Println( "fVal.NumField():", fVal.NumField()) //fVal.NumField(): 1
f := fVal.MethodByName("FillData")
f.Call([]reflect.Value{reflect.ValueOf(v)}) //panic: reflect: call of reflect.Value.Call on zero Value
}
The method is on the pointer receiver. The value fVal is a NestedType. Call Value.Addr to get a *NestedType:
f := fVal.Addr().MethodByName("FillData")
Run it on the playground.
I am new to Go & I am trying to learn how to cast interface{} to a Map. Here is an example of what I am trying to implement.
Playground link : https://play.golang.org/p/3jhKlGKO46Z
Thanks for the help.
package main
import (
"fmt"
)
func main() {
Map := make(map[string]interface{})
test(Map)
for k,v := range Map {
fmt.Println("key : %v Value : %v", k, v)
}
}
func test(v interface{}) error{
data := make(map[int]interface{})
for i := 0; i < 10; i++ {
data[i] = i * 5
}
for key,val := range data {
// fmt.Println("key : %v Value : %v", key, val)
v[key] = val
}
return nil
Go supports type assertions for the interfaces. It provides the concrete value present in the interface.
You can achieve this with following code.
m, ok := v.(map[int]interface{})
if ok {
// use m
_ = m
}
If the asserted value is not of given type, ok will be false
If you avoid second return value, the program will panic for wrong assertions.
I strongly recommend you to go through https://tour.golang.org
I am putting structs names and types into a map, Marshal this map into a JSON and Unmarshal this JSON back into a map which I can extract the types of each struct the same as before.
Here is what tried:
1 - have predefined structs
2 - define a map[string]interface{}
3 - populate this map with the struct name as key and struct type as interface() value.
4 - Marshaled this map into a JSON.
5 - Define a new map[string]interface{}
6 - Unmarshal the JSON into this new map for later use in the program.
My hope is to preserve the the reflect types of the interface{} the same.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// Gallery :
type Gallery struct {
ID int
Name string
Images []Image
}
// Image :
type Image struct {
Source string
Height int
Width int
}
func main() {
myMap := make(map[string]interface{})
myMap["Gallery"] = Gallery{}
// encode this map into JSON, jStr:
jStr, err := json.Marshal(&myMap)
if err != nil {
fmt.Println(err)
}
// print the JSON string, just for testing.
fmt.Printf("jStr is: %v \n", string(jStr))
// create a newMap and populate it with the JSON jStr
newMap := make(map[string]interface{})
err = json.Unmarshal(jStr, &newMap)
if err != nil {
fmt.Println(err)
}
fmt.Println()
// iterate over myMap and inspect the reflect type of v
for k, v := range myMap {
fmt.Printf("myMap: k is: %v \t v is: %v \n", k, v)
fmt.Printf("myMap: reflect.TypeOf(v) is: %v \n", reflect.TypeOf(v).Kind())
}
fmt.Println()
// iterate over newMap and inspect the reflect type of v
for k, v := range newMap {
fmt.Printf("newMap: k is: %v \t v is: %v \n", k, v)
fmt.Printf("newMap: reflect.TypeOf(v) is: %v \n", reflect.TypeOf(v).Kind())
fmt.Println()
}
The reflect.TypeOf(v).Kind() for both maps should be struct. But it is not the case. for myMap, the Kind is struct (which is correct), but for newMap, the Kind is map which is NOT correct. What am I missing here. Here is the full code on play.golang.org: https://play.golang.org/p/q8BBLlw3fMA
I am crawling a struct recursively. It's identical to what the json package does. If a nil pointer to a struct is encountered, the pointer should be set to the zero value of the struct to be able to continue digging. How can I achieve this?
var unmarshal func(s reflect.Value) error
unmarshal = func(s reflect.Value) error {
t := s.Type()
for i := 0; i < s.NumField(); i++ {
v := s.Field(i)
f := t.Field(i)
kind := v.Kind()
if key, ok := f.Tag.Lookup("env"); ok {
// ...
} else if kind == reflect.Struct {
unmarshal(v)
} else if kind == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
if v.IsNil() {
// Set pointer to zero value of struct.
}
unmarshal(v.Elem())
}
}
return nil
}
You can use reflect.New(f.Type.Elem()) to create a pointer to the zero value and then v.Set(value) to set it. As per the reflect docs:
New returns a Value representing a pointer to a new zero value for the specified type. That is, the returned Value's Type is PtrTo(typ).
Full example in Go Playground here:
https://play.golang.org/p/b-034h3I-cn
Note that I also did f.Type.Elem().Kind() in the if statement rather v.Elem().Kind() that you have as that is asking for the Kind() of nil which is invalid.