Go use string var to fill struct [duplicate] - go

Here is a simple go program that is not working :
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getProperty(&v, "X"))
}
func getProperty(v *Vertex, property string) (string) {
return v[property]
}
Error:
prog.go:18: invalid operation: v[property] (index of type *Vertex)
What I want is to access the Vertex X property using its name. If I do v.X it works, but v["X"] doesn't.
Can someone tell me how to make this work ?

Most code shouldn't need this sort of dynamic lookup. It's inefficient compared to direct access (the compiler knows the offset of the X field in a Vertex structure, it can compile v.X to a single machine instruction, whereas a dynamic lookup will need some sort of hash table implementation or similar). It's also inhibits static typing: the compiler has no way to check that you're not trying to access unknown fields dynamically, and it can't know what the resulting type should be.
But... the language provides a reflect module for the rare times you need this.
package main
import "fmt"
import "reflect"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getField(&v, "X"))
}
func getField(v *Vertex, field string) int {
r := reflect.ValueOf(v)
f := reflect.Indirect(r).FieldByName(field)
return int(f.Int())
}
There's no error checking here, so you'll get a panic if you ask for a field that doesn't exist, or the field isn't of type int. Check the documentation for reflect for more details.

You now have the project oleiade/reflections which allows you to get/set fields on struct value or pointers.
It makes using the reflect package less tricky.
s := MyStruct {
FirstField: "first value",
SecondField: 2,
ThirdField: "third value",
}
fieldsToExtract := []string{"FirstField", "ThirdField"}
for _, fieldName := range fieldsToExtract {
value, err := reflections.GetField(s, fieldName)
DoWhatEverWithThatValue(value)
}
// In order to be able to set the structure's values,
// a pointer to it has to be passed to it.
_ := reflections.SetField(&s, "FirstField", "new value")
// If you try to set a field's value using the wrong type,
// an error will be returned
err := reflection.SetField(&s, "FirstField", 123) // err != nil

With getAttr, you can get and set easy.
package main
import (
"fmt"
"reflect"
)
func getAttr(obj interface{}, fieldName string) reflect.Value {
pointToStruct := reflect.ValueOf(obj) // addressable
curStruct := pointToStruct.Elem()
if curStruct.Kind() != reflect.Struct {
panic("not struct")
}
curField := curStruct.FieldByName(fieldName) // type: reflect.Value
if !curField.IsValid() {
panic("not found:" + fieldName)
}
return curField
}
func main() {
type Point struct {
X int
y int // Set prefix to lowercase if you want to protect it.
Z string
}
p := Point{3, 5, "Z"}
pX := getAttr(&p, "X")
// Get test (int)
fmt.Println(pX.Int()) // 3
// Set test
pX.SetInt(30)
fmt.Println(p.X) // 30
// test string
getAttr(&p, "Z").SetString("Z123")
fmt.Println(p.Z) // Z123
py := getAttr(&p, "y")
if py.CanSet() { // The necessary condition for CanSet to return true is that the attribute of the struct must have an uppercase prefix
py.SetInt(50) // It will not execute here because CanSet return false.
}
fmt.Println(p.y) // 5
}
Run it on 👉
Reference
CanSet
A Good example of the reflex: https://stackoverflow.com/a/6396678

You can marshal the struct and unmarshal it back to map[string]interface{}. But, it would convert all the number values to float64 so you would have to convert it to int manually.
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getProperty(&v, "X"))
}
func getProperty(v *Vertex, property string) float64 {
m, _ := json.Marshal(v)
var x map[string]interface{}
_ = json.Unmarshal(m, &x)
return x[property].(float64)
}

I want to offer a different approach that is not using reflection:
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getProperty(&v, "X"))
}
type Getter func(v *Vertex) int
var VertexAccess = map[string]Getter{
"X": func(v *Vertex) int { return v.X },
"Y": func(v *Vertex) int { return v.Y },
}
func getProperty(v *Vertex, property string) int {
return VertexAccess[property](v)
}
https://go.dev/play/p/2E7LZBWx7yZ
This is an O(1) map lookup and a function call which should perform better than reflection. Obviously, you need some scaffolding code for every type you want to support. On the plus side, you can refactor your code easily; your function getProperty is a potential anti-pattern to https://martinfowler.com/bliki/TellDontAsk.html

Related

Is there a way to define a constraint on the pointer to the type parameter? [duplicate]

Now that type parameters are available on golang/go:master, I decided to give it a try. It seems that I'm running into a limitation I could not find in the Type Parameters Proposal. (Or I must have missed it).
I want to write a function which returns a slice of values of a generic type with the constraint of an interface type. If the passed type is an implementation with a pointer receiver, how can we instantiate it?
type SetGetter[V any] interface {
Set(V)
Get() V
}
// SetGetterSlice turns a slice of type V into a slice of type T,
// with T.Set() called for each entry in values.
func SetGetterSlice[V any, T SetGetter[V]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
out[i].Set(v) // panic if T has pointer receiver!
}
return out
}
When calling the above SetGetterSlice() function with the *Count type as T, this code will panic upon calling Set(v). (Go2go playground) To no surprise, as basically the code created a slice of nil pointers:
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func main() {
ints := []int{1, 2, 3, 4, 5}
sgs := SetGetterSlice[int, *Count](ints)
for _, s := range sgs {
fmt.Println(s.Get())
}
}
Variations of the same problem
This ideas won't work, and I can't seem to find any simple way to instantiate the pointed value.
out[i] = new(T) will result in a compile failure, as it returns a *T where the type checker wants to see T.
Calling *new(T), compiles but will result in the same runtime panic because new(T) returns **Count in this case, where the pointer to Count is still nil.
Changing the return type to a slice of pointer to T will result in a compile failure:
func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T {
out := make([]*T, len(values))
for i, v := range values {
out[i] = new(T)
out[i].Set(v) // panic if T has pointer receiver
}
return out
}
func main() {
ints := []int{1, 2, 3, 4, 5}
SetGetterSlice[int, Count](ints)
// Count does not satisfy SetGetter[V]: wrong method signature
}
Workaround
The only solution I found until now, is to require a constructor function to be passed to the generic function. But this just feels wrong and a bit tedious. Why would this be required if func F(T interface{})() []T is perfectly valid syntax?
func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T {
out := make([]T, len(values))
for i, v := range values {
out[i] = constructor()
out[i].Set(v)
}
return out
}
// ...
func main() {
ints := []int{1, 2, 3, 4, 5}
SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) })
}
Summary
My questions, in order of priority:
Am I overlooking something obvious?
Is this a limitation of generics in Go and this is as good as it gets?
Is this limitation known or should I raise an issue at the Go project?
Basically you have to add one more type parameter to the constraint to make T convertible to its pointer type. In its most basic form, this technique looks like the following (with an anonymous constraint):
func Foo[T any, PT interface { *T; M() }]() {
p := PT(new(T))
p.M() // calling method on non-nil pointer
}
Playground: https://go.dev/play/p/L00tePwrDfx
Step by step solution
Your constraint SetGetter already declares a type param V, so we slightly modify the example above:
// V is your original type param
// T is the additional helper param
type SetGetter[V any, T any] interface {
Set(V)
Get() V
*T
}
Then you define the SetGetterSlice function with the type parameter T any, whose purpose is just to instantiate the constraint SetGetter.
You will then be able to convert the expression &out[i] to the pointer type, and successfully call the method on the pointer receiver:
// T is the type with methods with pointer receiver
// PT is the SetGetter constraint with *T
func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
// out[i] has type T
// &out[i] has type *T
// PT constraint includes *T
p := PT(&out[i]) // valid conversion!
p.Set(v) // calling with non-nil pointer receiver
}
return out
}
Full program:
package main
import (
"fmt"
)
type SetGetter[V any, T any] interface {
Set(V)
Get() V
*T
}
func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
p := PT(&out[i])
p.Set(v)
}
return out
}
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func main() {
ints := []int{1, 2, 3, 4, 5}
// instantiate with base type
sgs := SetGetterSlice[int, Count](ints)
for _, s := range sgs {
fmt.Println(s.Get()) // prints 1,2,3,4,5 each in a newline
}
}
This becomes more verbose because SetGetterSlice now requires three type parameters: the original V plus T (the type with pointer receivers) and PT (the new constraint). However when you call the function, you can omit the third one – with type inference, both type params V and T required to instantiate PT SetGetter[V,T] are already known:
SetGetterSlice[int, Count](ints)
Playground: https://go.dev/play/p/gcQZnw07Wp3
Spent a few hours to understand it.
So decided to add my example.
package main
import (
"fmt"
)
type User struct {
FullName string
Removed bool
}
type Account struct {
Name string
Removed bool
}
type Scanner[T User | Account] interface {
Scan()
*T
}
type Model interface {
User | Account
}
func (user *User) Scan() {
user.FullName = `changed in scan method`
user.Removed = true
}
func (account *Account) Scan() {
account.Name = `changed in scan method`
account.Removed = true
}
func setRemovedState[T Model, PT Scanner[T]](state bool) *T {
var obj T
pointer := PT(&obj)
pointer.Scan() // calling method on non-nil pointer
return &obj
}
func main() {
user := setRemovedState[User](true)
account := setRemovedState[Account](true)
fmt.Printf("User: %v\n", *user)
fmt.Printf("Account: %v\n", *account)
}
you can also try to attack the problem slighty differently, to keep it simple.
package main
import (
"fmt"
)
func mapp[T any, V any](s []T, h func(T) V) []V {
z := make([]V, len(s))
for i, v := range s {
z[i] = h(v)
}
return z
}
func mappp[T any, V any](s []T, h func(T) V) []V {
z := make([]V, 0, len(s))
for _, v := range s {
z = append(z, h(v))
}
return z
}
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func FromInt(x int) *Count {
var out Count
out.x = x
return &out
}
func main() {
ints := []int{1, 2, 3, 4, 5}
sgs := mapp(ints, FromInt)
fmt.Printf("%T\n",sgs)
for _, s := range sgs {
fmt.Println(s.Get())
}
fmt.Println()
sgs = mappp(ints, FromInt)
fmt.Printf("%T\n",sgs)
for _, s := range sgs {
fmt.Println(s.Get())
}
}
https://go2goplay.golang.org/p/vzViKwiJJkZ
It is like your func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T but without the complex verbosity. It also gave me zero pain to solve.
Edit: see blackgreen's answer, which I also found later on my own while scanning through the same documentation they linked. I was going to edit this answer to update based on that, but now I don't have to. :-)
There is probably a better way—this one seems a bit clumsy—but I was able to work around this with reflect:
if reflect.TypeOf(out[0]).Kind() == reflect.Ptr {
x := reflect.ValueOf(out).Index(i)
x.Set(reflect.New(reflect.TypeOf(out[0]).Elem()))
}
I just added the above four lines to your example. The temporary variable is left over from some debug and obviously can be removed. Playground link

How can I instantiate a non-nil pointer of type argument with generic Go?

Now that type parameters are available on golang/go:master, I decided to give it a try. It seems that I'm running into a limitation I could not find in the Type Parameters Proposal. (Or I must have missed it).
I want to write a function which returns a slice of values of a generic type with the constraint of an interface type. If the passed type is an implementation with a pointer receiver, how can we instantiate it?
type SetGetter[V any] interface {
Set(V)
Get() V
}
// SetGetterSlice turns a slice of type V into a slice of type T,
// with T.Set() called for each entry in values.
func SetGetterSlice[V any, T SetGetter[V]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
out[i].Set(v) // panic if T has pointer receiver!
}
return out
}
When calling the above SetGetterSlice() function with the *Count type as T, this code will panic upon calling Set(v). (Go2go playground) To no surprise, as basically the code created a slice of nil pointers:
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func main() {
ints := []int{1, 2, 3, 4, 5}
sgs := SetGetterSlice[int, *Count](ints)
for _, s := range sgs {
fmt.Println(s.Get())
}
}
Variations of the same problem
This ideas won't work, and I can't seem to find any simple way to instantiate the pointed value.
out[i] = new(T) will result in a compile failure, as it returns a *T where the type checker wants to see T.
Calling *new(T), compiles but will result in the same runtime panic because new(T) returns **Count in this case, where the pointer to Count is still nil.
Changing the return type to a slice of pointer to T will result in a compile failure:
func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T {
out := make([]*T, len(values))
for i, v := range values {
out[i] = new(T)
out[i].Set(v) // panic if T has pointer receiver
}
return out
}
func main() {
ints := []int{1, 2, 3, 4, 5}
SetGetterSlice[int, Count](ints)
// Count does not satisfy SetGetter[V]: wrong method signature
}
Workaround
The only solution I found until now, is to require a constructor function to be passed to the generic function. But this just feels wrong and a bit tedious. Why would this be required if func F(T interface{})() []T is perfectly valid syntax?
func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T {
out := make([]T, len(values))
for i, v := range values {
out[i] = constructor()
out[i].Set(v)
}
return out
}
// ...
func main() {
ints := []int{1, 2, 3, 4, 5}
SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) })
}
Summary
My questions, in order of priority:
Am I overlooking something obvious?
Is this a limitation of generics in Go and this is as good as it gets?
Is this limitation known or should I raise an issue at the Go project?
Basically you have to add one more type parameter to the constraint to make T convertible to its pointer type. In its most basic form, this technique looks like the following (with an anonymous constraint):
func Foo[T any, PT interface { *T; M() }]() {
p := PT(new(T))
p.M() // calling method on non-nil pointer
}
Playground: https://go.dev/play/p/L00tePwrDfx
Step by step solution
Your constraint SetGetter already declares a type param V, so we slightly modify the example above:
// V is your original type param
// T is the additional helper param
type SetGetter[V any, T any] interface {
Set(V)
Get() V
*T
}
Then you define the SetGetterSlice function with the type parameter T any, whose purpose is just to instantiate the constraint SetGetter.
You will then be able to convert the expression &out[i] to the pointer type, and successfully call the method on the pointer receiver:
// T is the type with methods with pointer receiver
// PT is the SetGetter constraint with *T
func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
// out[i] has type T
// &out[i] has type *T
// PT constraint includes *T
p := PT(&out[i]) // valid conversion!
p.Set(v) // calling with non-nil pointer receiver
}
return out
}
Full program:
package main
import (
"fmt"
)
type SetGetter[V any, T any] interface {
Set(V)
Get() V
*T
}
func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
p := PT(&out[i])
p.Set(v)
}
return out
}
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func main() {
ints := []int{1, 2, 3, 4, 5}
// instantiate with base type
sgs := SetGetterSlice[int, Count](ints)
for _, s := range sgs {
fmt.Println(s.Get()) // prints 1,2,3,4,5 each in a newline
}
}
This becomes more verbose because SetGetterSlice now requires three type parameters: the original V plus T (the type with pointer receivers) and PT (the new constraint). However when you call the function, you can omit the third one – with type inference, both type params V and T required to instantiate PT SetGetter[V,T] are already known:
SetGetterSlice[int, Count](ints)
Playground: https://go.dev/play/p/gcQZnw07Wp3
Spent a few hours to understand it.
So decided to add my example.
package main
import (
"fmt"
)
type User struct {
FullName string
Removed bool
}
type Account struct {
Name string
Removed bool
}
type Scanner[T User | Account] interface {
Scan()
*T
}
type Model interface {
User | Account
}
func (user *User) Scan() {
user.FullName = `changed in scan method`
user.Removed = true
}
func (account *Account) Scan() {
account.Name = `changed in scan method`
account.Removed = true
}
func setRemovedState[T Model, PT Scanner[T]](state bool) *T {
var obj T
pointer := PT(&obj)
pointer.Scan() // calling method on non-nil pointer
return &obj
}
func main() {
user := setRemovedState[User](true)
account := setRemovedState[Account](true)
fmt.Printf("User: %v\n", *user)
fmt.Printf("Account: %v\n", *account)
}
you can also try to attack the problem slighty differently, to keep it simple.
package main
import (
"fmt"
)
func mapp[T any, V any](s []T, h func(T) V) []V {
z := make([]V, len(s))
for i, v := range s {
z[i] = h(v)
}
return z
}
func mappp[T any, V any](s []T, h func(T) V) []V {
z := make([]V, 0, len(s))
for _, v := range s {
z = append(z, h(v))
}
return z
}
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func FromInt(x int) *Count {
var out Count
out.x = x
return &out
}
func main() {
ints := []int{1, 2, 3, 4, 5}
sgs := mapp(ints, FromInt)
fmt.Printf("%T\n",sgs)
for _, s := range sgs {
fmt.Println(s.Get())
}
fmt.Println()
sgs = mappp(ints, FromInt)
fmt.Printf("%T\n",sgs)
for _, s := range sgs {
fmt.Println(s.Get())
}
}
https://go2goplay.golang.org/p/vzViKwiJJkZ
It is like your func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T but without the complex verbosity. It also gave me zero pain to solve.
Edit: see blackgreen's answer, which I also found later on my own while scanning through the same documentation they linked. I was going to edit this answer to update based on that, but now I don't have to. :-)
There is probably a better way—this one seems a bit clumsy—but I was able to work around this with reflect:
if reflect.TypeOf(out[0]).Kind() == reflect.Ptr {
x := reflect.ValueOf(out).Index(i)
x.Set(reflect.New(reflect.TypeOf(out[0]).Elem()))
}
I just added the above four lines to your example. The temporary variable is left over from some debug and obviously can be removed. Playground link

Get name of struct field using reflection

What is the way of printing "Foo" here? In this example, what prints is "string".
http://play.golang.org/p/ZnK6PRwEPp
type A struct {
Foo string
}
func (a *A) PrintFoo() {
fmt.Println("Foo value is " + a.Foo)
}
func main() {
a := &A{Foo: "afoo"}
val := reflect.Indirect(reflect.ValueOf(a))
fmt.Println(val.Field(0).Type().Name())
}
You want val.Type().Field(0).Name. The Field method on reflect.Type will return a struct describing that field, which includes the name, among other information.
There is no way to retrieve the field name for a reflect.Value representing a particular field value, since that is a property of the containing struct.
I think the better way to get the fields' name in the struct is
func main() {
a := &A{Foo: "afoo"}
val := reflect.ValueOf(a).Elem()
for i:=0; i<val.NumField();i++{
fmt.Println(val.Type().Field(i).Name)
}
}
There are two tips:
use .Elem() after you reflect.ValueOf(a), because in your case, a is a pointer.
val.Field(i).Type().Name is totally different from val.Type().Field(i).Name. The latter one can get the name of the field in the struct
Hope that it is helpful..
If you want to have a look at more cases, please check my 2mins article
You need to Get the Field of the Type Definition not of the Value.
http://play.golang.org/p/7Bc7MJikbJ
package main
import "fmt"
import "reflect"
type A struct {
Foo string
}
func (a *A) PrintFoo() {
fmt.Println("Foo value is " + a.Foo)
}
func main() {
a := &A{Foo: "afoo"}
val := reflect.Indirect(reflect.ValueOf(a))
fmt.Println(val.Type().Field(0).Name)
}
With the new Names method of the structs package it's even more easier:
package main
import (
"fmt"
"github.com/fatih/structs"
)
type A struct {
Foo string
Bar int
}
func main() {
names := structs.Names(&A{})
fmt.Println(names) // ["Foo", "Bar"]
}
You can also use https://github.com/fatih/structs
// Convert the fields of a struct to a []*Field
fields := s.Fields()
for _, f := range fields {
fmt.Printf("field name: %+v\n", f.Name())
}
package main
import "fmt"
import "reflect"
type A struct {
Foo string
}
func (a *A) PrintFoo() {
fmt.Println("Foo value is " + a.Foo)
}
func main() {
a := &A{Foo: "afoo"}
//long and bored code
t := reflect.TypeOf(*a)
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Name)
}
} else {
fmt.Println("not a stuct")
}
//shorthanded call
fmt.Println(reflect.TypeOf(*a).Field(0).Name)//can panic if no field exists
}
You can use this function, which takes the struct as the first parameter, and then its fields. It returns the map type, which is convenient to use
If you use fields from another structure, nothing will happen
If you try to use a different type, it will cause panic
Note that the field has an ordinal number according to the list (starting from 0). All fields in the structure must start with uppercase
func GetStructFieldName(Struct interface{}, StructField ...interface{}) (fields map[int]string) {
fields = make(map[int]string)
s := reflect.ValueOf(Struct).Elem()
for r := range StructField {
f := reflect.ValueOf(StructField[r]).Elem()
for i := 0; i < s.NumField(); i++ {
valueField := s.Field(i)
if valueField.Addr().Interface() == f.Addr().Interface() {
fields[i] = s.Type().Field(i).Name
}
}
}
return fields
}
Full example and playground
package main
import (
"fmt"
"reflect"
)
type Example struct {
Apple bool
Pear int
}
func GetStructFieldName(Struct interface{}, StructField ...interface{}) (fields map[int]string) {
fields = make(map[int]string)
for r := range StructField {
s := reflect.ValueOf(Struct).Elem()
f := reflect.ValueOf(StructField[r]).Elem()
for i := 0; i < s.NumField(); i++ {
valueField := s.Field(i)
if valueField.Addr().Interface() == f.Addr().Interface() {
fields[i] = s.Type().Field(i).Name
}
}
}
return fields
}
func main() {
e := Example{}
names := GetStructFieldName(&e, &e.Apple, &e.Pear)
fmt.Println(names)
fmt.Println(names[0], names[1])
for i := range names {
fmt.Println(names[i])
}
/* Output:
map[0:Apple 1:Pear]
Apple Pear
Apple
Pear
*/
}

Access struct property by name

Here is a simple go program that is not working :
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getProperty(&v, "X"))
}
func getProperty(v *Vertex, property string) (string) {
return v[property]
}
Error:
prog.go:18: invalid operation: v[property] (index of type *Vertex)
What I want is to access the Vertex X property using its name. If I do v.X it works, but v["X"] doesn't.
Can someone tell me how to make this work ?
Most code shouldn't need this sort of dynamic lookup. It's inefficient compared to direct access (the compiler knows the offset of the X field in a Vertex structure, it can compile v.X to a single machine instruction, whereas a dynamic lookup will need some sort of hash table implementation or similar). It's also inhibits static typing: the compiler has no way to check that you're not trying to access unknown fields dynamically, and it can't know what the resulting type should be.
But... the language provides a reflect module for the rare times you need this.
package main
import "fmt"
import "reflect"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getField(&v, "X"))
}
func getField(v *Vertex, field string) int {
r := reflect.ValueOf(v)
f := reflect.Indirect(r).FieldByName(field)
return int(f.Int())
}
There's no error checking here, so you'll get a panic if you ask for a field that doesn't exist, or the field isn't of type int. Check the documentation for reflect for more details.
You now have the project oleiade/reflections which allows you to get/set fields on struct value or pointers.
It makes using the reflect package less tricky.
s := MyStruct {
FirstField: "first value",
SecondField: 2,
ThirdField: "third value",
}
fieldsToExtract := []string{"FirstField", "ThirdField"}
for _, fieldName := range fieldsToExtract {
value, err := reflections.GetField(s, fieldName)
DoWhatEverWithThatValue(value)
}
// In order to be able to set the structure's values,
// a pointer to it has to be passed to it.
_ := reflections.SetField(&s, "FirstField", "new value")
// If you try to set a field's value using the wrong type,
// an error will be returned
err := reflection.SetField(&s, "FirstField", 123) // err != nil
With getAttr, you can get and set easy.
package main
import (
"fmt"
"reflect"
)
func getAttr(obj interface{}, fieldName string) reflect.Value {
pointToStruct := reflect.ValueOf(obj) // addressable
curStruct := pointToStruct.Elem()
if curStruct.Kind() != reflect.Struct {
panic("not struct")
}
curField := curStruct.FieldByName(fieldName) // type: reflect.Value
if !curField.IsValid() {
panic("not found:" + fieldName)
}
return curField
}
func main() {
type Point struct {
X int
y int // Set prefix to lowercase if you want to protect it.
Z string
}
p := Point{3, 5, "Z"}
pX := getAttr(&p, "X")
// Get test (int)
fmt.Println(pX.Int()) // 3
// Set test
pX.SetInt(30)
fmt.Println(p.X) // 30
// test string
getAttr(&p, "Z").SetString("Z123")
fmt.Println(p.Z) // Z123
py := getAttr(&p, "y")
if py.CanSet() { // The necessary condition for CanSet to return true is that the attribute of the struct must have an uppercase prefix
py.SetInt(50) // It will not execute here because CanSet return false.
}
fmt.Println(p.y) // 5
}
Run it on 👉
Reference
CanSet
A Good example of the reflex: https://stackoverflow.com/a/6396678
You can marshal the struct and unmarshal it back to map[string]interface{}. But, it would convert all the number values to float64 so you would have to convert it to int manually.
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getProperty(&v, "X"))
}
func getProperty(v *Vertex, property string) float64 {
m, _ := json.Marshal(v)
var x map[string]interface{}
_ = json.Unmarshal(m, &x)
return x[property].(float64)
}
I want to offer a different approach that is not using reflection:
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(getProperty(&v, "X"))
}
type Getter func(v *Vertex) int
var VertexAccess = map[string]Getter{
"X": func(v *Vertex) int { return v.X },
"Y": func(v *Vertex) int { return v.Y },
}
func getProperty(v *Vertex, property string) int {
return VertexAccess[property](v)
}
https://go.dev/play/p/2E7LZBWx7yZ
This is an O(1) map lookup and a function call which should perform better than reflection. Obviously, you need some scaffolding code for every type you want to support. On the plus side, you can refactor your code easily; your function getProperty is a potential anti-pattern to https://martinfowler.com/bliki/TellDontAsk.html

Iterate through the fields of a struct in Go

Basically, the only way (that I know of) to iterate through the values of the fields of a struct is like this:
type Example struct {
a_number uint32
a_string string
}
//...
r := &Example{(2 << 31) - 1, "...."}:
for _, d:= range []interface{}{ r.a_number, r.a_string, } {
//do something with the d
}
I was wondering, if there's a better and more versatile way of achieving []interface{}{ r.a_number, r.a_string, }, so I don't need to list each parameter individually, or alternatively, is there a better way to loop through a struct?
I tried to look through the reflect package, but I hit a wall, because I'm not sure what to do once I retrieve reflect.ValueOf(*r).Field(0).
Thanks!
After you've retrieved the reflect.Value of the field by using Field(i) you can get a
interface value from it by calling Interface(). Said interface value then represents the
value of the field.
There is no function to convert the value of the field to a concrete type as there are,
as you may know, no generics in go. Thus, there is no function with the signature GetValue() T
with T being the type of that field (which changes of course, depending on the field).
The closest you can achieve in go is GetValue() interface{} and this is exactly what reflect.Value.Interface()
offers.
The following code illustrates how to get the values of each exported field in a struct
using reflection (play):
import (
"fmt"
"reflect"
)
func main() {
x := struct{Foo string; Bar int }{"foo", 2}
v := reflect.ValueOf(x)
values := make([]interface{}, v.NumField())
for i := 0; i < v.NumField(); i++ {
values[i] = v.Field(i).Interface()
}
fmt.Println(values)
}
If you want to Iterate through the Fields and Values of a struct then you can use the below Go code as a reference.
package main
import (
"fmt"
"reflect"
)
type Student struct {
Fname string
Lname string
City string
Mobile int64
}
func main() {
s := Student{"Chetan", "Kumar", "Bangalore", 7777777777}
v := reflect.ValueOf(s)
typeOfS := v.Type()
for i := 0; i< v.NumField(); i++ {
fmt.Printf("Field: %s\tValue: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface())
}
}
Run in playground
Note: If the Fields in your struct are not exported then the v.Field(i).Interface() will give panic panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.
Go 1.17 (Q3 2021) should add a new option, through commit 009bfea and CL 281233, fixing issue 42782.
reflect: add VisibleFields function
When writing code that reflects over a struct type, it's a common requirement to know the full set of struct fields, including fields available due to embedding of anonymous members while excluding fields that are erased because they're at the same level as another field with the same name.
The logic to do this is not that complex, but it's a little subtle and easy to get wrong.
This CL adds a new reflect.VisibleFields() function to the reflect package that returns the full set of effective fields that apply in a given struct type.
fields := reflect.VisibleFields(typ)
for j, field := range fields {
...
}
Example,
type employeeDetails struct {
id int16
name string
designation string
}
func structIterator() {
fields := reflect.VisibleFields(reflect.TypeOf(struct{ employeeDetails }{}))
for _, field := range fields {
fmt.Printf("Key: %s\tType: %s\n", field.Name, field.Type)
}
}
Maybe too late :))) but there is another solution that you can find the key and value of structs and iterate over that
package main
import (
"fmt"
"reflect"
)
type person struct {
firsName string
lastName string
iceCream []string
}
func main() {
u := struct {
myMap map[int]int
mySlice []string
myPerson person
}{
myMap: map[int]int{1: 10, 2: 20},
mySlice: []string{"red", "green"},
myPerson: person{
firsName: "Esmaeil",
lastName: "Abedi",
iceCream: []string{"Vanilla", "chocolate"},
},
}
v := reflect.ValueOf(u)
for i := 0; i < v.NumField(); i++ {
fmt.Println(v.Type().Field(i).Name)
fmt.Println("\t", v.Field(i))
}
}
and there is no *panic* for v.Field(i)
use this:
type x struct {
Id int
jsj int
}
func main() {
x2 := x{jsj: 10, Id: 5}
v := reflect.ValueOf(x2)
for i := 0; i < v.NumField(); i++ {
fmt.Println(v.Field(i))
}
}
====>10
====>5
Taking Chetan Kumar solution and in case you need to apply to a map[string]int
package main
import (
"fmt"
"reflect"
)
type BaseStats struct {
Hp int
HpMax int
Mp int
MpMax int
Strength int
Speed int
Intelligence int
}
type Stats struct {
Base map[string]int
Modifiers []string
}
func StatsCreate(stats BaseStats) Stats {
s := Stats{
Base: make(map[string]int),
}
//Iterate through the fields of a struct
v := reflect.ValueOf(stats)
typeOfS := v.Type()
for i := 0; i< v.NumField(); i++ {
val := v.Field(i).Interface().(int)
s.Base[typeOfS.Field(i).Name] = val
}
return s
}
func (s Stats) GetBaseStat(id string) int {
return s.Base[id]
}
func main() {
m := StatsCreate(BaseStats{300, 300, 300, 300, 10, 10, 10})
fmt.Println(m.GetBaseStat("Hp"))
}
Use reflect package. First, get the type of variable with reflect.TypeOf and get numbers of elements with reflect.NumField.To obtain the values of the fields iteratively of a structure must reflect the variable and use the function rg.Elem().Field(i)
package main
import (
"fmt"
"reflect"
)
type Gopher struct {
Name string
Color string
Year int
}
func main() {
g := Gopher{Name: "AAA", Color: "BBBB", Year: 2021}
gtype := reflect.TypeOf(g)
numFields := gtype.NumField()
rg := reflect.ValueOf(&g)
for i := 0; i < numFields; i++ {
fmt.Println(rg.Elem().Field(i))
}
}
In Go, you can use the reflect package to iterate through the fields of a struct. The reflect package allows you to inspect the properties of values at runtime, including their type and value. Here's an example of how to iterate through the fields of a struct:
Go Playground
package main
import (
"fmt"
"reflect"
)
type Movie struct {
Name string
Year int
}
func main() {
p := Movie{"The Dark Knight", 2008}
val := reflect.ValueOf(p)
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
fmt.Printf("Field Name: %s, Field Value: %v\n", fieldType.Name, field.Interface())
}
}
Output:
Field Name: Name, Field Value: The Dark Knight
Field Name: Age, Field Value: 2008

Resources