having a rough time working with struct fields using reflect package. in particular, have not figured out how to set the field value.
type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
getting Name of field i - this seems to work
var field = reflect.TypeOf(r).Field(i).Name
getting value of field i as a) interface{}, b) int - this seems to work
var iface interface{} = reflect.ValueOf(r).Field(i).Interface()
var i int = int(reflect.ValueOf(r).Field(i).Int())
setting value of field i - try one - panic
reflect.ValueOf(r).Field(i).SetInt( i64 )
panic: reflect.Value·SetInt using value obtained using unexported field
assuming it did not like field names "id" and "name", so renamed to "Id" and "Name"
a) is this assumption correct?
b) if correct, thought not necessary since in same file / package
setting value of field i - try two (with field names capitalized ) - panic
reflect.ValueOf(r).Field(i).SetInt( 465 )
reflect.ValueOf(r).Field(i).SetInt( i64 )
panic: reflect.Value·SetInt using unaddressable value
Instructions below by #peterSO are thorough and high quality
Four. this works:
reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )
he documents as well that the field names must be exportable (begin with capital letter)
The Go json package marshals and unmarshals JSON from and to Go structures.
Here's a step-by-step example which sets the value of a struct field while carefully avoiding errors.
The Go reflect package has a CanAddr function.
func (v Value) CanAddr() bool
CanAddr returns true if the value's
address can be obtained with Addr.
Such values are called addressable. A
value is addressable if it is an
element of a slice, an element of an
addressable array, a field of an
addressable struct, or the result of
dereferencing a pointer. If CanAddr
returns false, calling Addr will
panic.
The Go reflect package has a CanSet function, which, if true, implies that CanAddr is also true.
func (v Value) CanSet() bool
CanSet returns true if the value of v
can be changed. A Value can be changed
only if it is addressable and was not
obtained by the use of unexported
struct fields. If CanSet returns
false, calling Set or any
type-specific setter (e.g., SetBool,
SetInt64) will panic.
We need to make sure we can Set the struct field. For example,
package main
import (
"fmt"
"reflect"
)
func main() {
type t struct {
N int
}
var n = t{42}
// N at start
fmt.Println(n.N)
// pointer to struct - addressable
ps := reflect.ValueOf(&n)
// struct
s := ps.Elem()
if s.Kind() == reflect.Struct {
// exported field
f := s.FieldByName("N")
if f.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if f.CanSet() {
// change value of N
if f.Kind() == reflect.Int {
x := int64(7)
if !f.OverflowInt(x) {
f.SetInt(x)
}
}
}
}
}
// N at end
fmt.Println(n.N)
}
Output:
42
7
If we can be certain that all the error checks are unnecessary, the example simplifies to,
package main
import (
"fmt"
"reflect"
)
func main() {
type t struct {
N int
}
var n = t{42}
fmt.Println(n.N)
reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
fmt.Println(n.N)
}
BTW, Go is available as open source code. A good way to learn about reflection is to see how the core Go developers use it. For example, the Go fmt and json packages. The package documentation has links to the source code files under the heading Package files.
This seems to work:
package main
import (
"fmt"
"reflect"
)
type Foo struct {
Number int
Text string
}
func main() {
foo := Foo{123, "Hello"}
fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)
fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}
Prints:
123
321
Related
I've the following struct:
type testCase struct {
input string
isValid bool
}
I want to use this struct in multiple tests and input could be either a string or an intetc.
I can convert the int input to string and convert it back to int while processing, or I can define two different structs e.g. testCaseInt and testCaseStruct which will solve my problem but how do I solve this by converting input to an interface?
I'm new to Go and tried Googling about this but couldn't find maybe because I don't know what to search for.
How to declare and use a variable which can store both string and int values in Go?
You cannot. Go's type system (as of Go 1.17) doesn't provide sum types.
You will have to wait for Go 1.18.
tl;dr the trade-off is between static typing and flexible containers.
Up to Go 1.17 you cannot have a struct field with different static types. The best you can have is interface{}, and then assert the dynamic type upon usage. This effectively allows you to have containers of testCases with either type at run time.
type testCase struct {
input interface{}
isValid bool
}
func main() {
// can initialize container with either type
cases := []testCase{{500, false}, {"foobar", true}}
// must type-assert when using
n := cases[0].(int)
s := cases[1].(string)
}
With Go 1.18, you can slightly improve on type safety, in exchange for less flexibility.
Parametrize the struct with a union. This statically restricts the allowed types, but the struct now must be instantiated explicitly, so you can't have containers with different instantiations. This may or may not be compatible with your goals.
type testCase[T int | string] struct {
input T
isValid bool
}
func main() {
// must instantiate with a concrete type
cases := []testCase[int]{
{500, false}, // ok, field takes int value
/*{"foobar", true}*/, // not ok, "foobar" not assignable to int
}
// cases is a slice of testCase with int fields
}
No, instantiating as testCase[any] is a red herring. First of all, any just doesn't satisfy the constraint int | string; even if you relax that, it's actually worse than the Go 1.17 solution, because now instead of using just testCase in function arguments, you must use exactly testCase[any].
Parametrize the struct with a union but still use interface{}/any as field type: (How) can I implement a generic `Either` type in go? . This also doesn't allow to have containers with both types.
In general, if your goal is to have flexible container types (slices, maps, chans) with either type, you have to keep the field as interface{}/any and assert on usage. If you just want to reuse code with static typing at compile-time and you are on Go 1.18, use the union constraint.
Method 1:
package main
import (
"fmt"
)
func main() {
var a interface{}
a = "hi"
if valString, ok := a.(string); ok {
fmt.Printf("String: %s", valString)
}
a = 1
if valInt, ok := a.(int); ok {
fmt.Printf("\nInteger: %d", valInt)
}
}
Method 2:
package main
import (
"fmt"
)
func main() {
print("hi")
print(1)
}
func print(a interface{}) {
switch t := a.(type) {
case int:
fmt.Printf("Integer: %v\n", t)
case string:
fmt.Printf("String: %v\n", t)
}
}
only you can do is this, change string with interface{}
check on play (it works fine)
https://go.dev/play/p/pwSZiZp5oVx
package main
import "fmt"
type testCase struct {
input interface{}
isValid bool
}
func main() {
test1 := testCase{}
test1.input = "STRING". // <-------------------STRING
fmt.Printf("input: %v \n", test1)
test2 := testCase{}
test2.input = 1 // <-------------------INT
fmt.Printf("input: %v \n", test2)
}
I have a big.Int I need to store inside of a struct, but when I try to do so it overflows. Code example below
type NumberStore struct {
mainnumber *big.Int
}
var ledger NumberStore
// In decimal this is 33753000000000000000
var largehex string = "1D46ABEAB3FC28000"
myNumber := new(big.Int)
myNumber.SetString(largehex, 16)
ledger.mainnumber = myNumber
fmt.Println(ledger)// Prints 0xc0000a64c0, but I need it to be 33753000000000000000
Since mainnumber is a pointer field in your NumberStore struct, printing out the struct by default will just print out the value of the pointer, not the value it points to.
As the comment says, if you make your field exported then fmt.Println will show the underlying value; but if you don't need it exported, then fmt.Println(ledger.mainnumber) should print the number you expect. Here's your full code with one line added at the end:
package main
import (
"fmt"
"math/big"
)
type NumberStore struct {
mainnumber *big.Int
}
func main() {
var ledger NumberStore
// In decimal this is 33753000000000000000
var largehex string = "1D46ABEAB3FC28000"
myNumber := new(big.Int)
myNumber.SetString(largehex, 16)
ledger.mainnumber = myNumber
fmt.Println(ledger)
fmt.Println(ledger.mainnumber)
}
Run on the Playground, it prints:
{0xc000092000}
33753000000000000000
By printing like this fmt.Println(ledger), you're relying on the default formatting of the value ledger. For each field in the struct, it will only print the default representation of that value, unless it can access the appropriate custom formatting code for that type. For mainnumber of type *big.Int, that is "pointer to big.Int", it's simply printing the pointer address.
In order to give fmt access to the custom string formatting code for a *big.Int value, you either need to pass it directly: fmt.Println(ledger.mainnumber), or change mainnumber to an exported field, like this:
type NumberStore struct {
Mainnumber *big.Int
}
The fmt package cannot automatically find the value's formatting code (the .String() string method) if it is an unexported struct field.
Iterate through the fields of a struct in Go
I have read above thread and now I'm trying to extend it by processing multiple items at a time
package main
import (
"fmt"
"reflect"
)
type BaseStats struct {
value1 int
value2 byte
value3 int
value4 byte
}
func StatsCreate(stats BaseStats) {
v := reflect.ValueOf(stats)
val := make([]interface{}, v.NumField())
for i := 0; i< v.NumField(); i+=2 {
val[i+0] = v.Field(i+0).Interface().(int)
val[i+1] = v.Field(i+1).Interface().(byte)
fmt.Printf("%v %v", val[i+0], val[i+1])
}
}
func main() {
StatsCreate(BaseStats{20, '%', 400, '$'})
}
Error I had before: panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
Go requires you to make the first letter of any variable or function that you wish to use outside of the package (aka EXPORTED). Because you're trying to pass variables to another package, in this case the reflect package, but they're not exportable. It's very common that all struct field names are capitalized as best practice.
The capitalized->exported paradigm is one of the few things I don't like about the language, but that's the way it is.
package main
main.go
import (
"fmt"
"practice/pkg"
)
func main() {
mk := pkg.MustKey{map[string]string{"Hello": "bar"}}
fmt.Printf("%v\n", mk)
}
pkg package
hello.go
package pkg
type MustKey struct {
m map[string]string
}
While executing the following, I am getting error as mentioned in the subject line. Any help will be appreciated.
There is a very important rule in Go - how to Export/unexport any functions/methods/fields.
Export - when the name starts with a Captial letter (say it Public)
unexport - when the name starts with a small letter (say it Private)
So in your case, the struct type name MustKey is exportable (starts with a capital M) and can be accessed outside your defined package pkg. But the map variable m inside the struct does start with a small m, so it cannot be accessed outside the package and private to that package only.
So, you have 2 solutions:
Either use M instead of m, like:
type MustKey struct {
M map[string]string
}
Or, if you still want the map variable private - use Exported methods with helping of interface
type MustKey struct {
m map[string]string
}
func (mk *MustKey) GetValue(key string) (string, error) {
value, ok := m[key]
if !ok {
return "", fmt.Errorf("Key is not available: %s", key)
}
return value, nil
}
func (mk *MustKey) SetValue(key, value string) {
m[key] = value
}
And you can use these Get and Set methods to put your own logic.
Read this for good understanding.
In this code:
type MustKey struct {
m map[string]string
}
the map variable is in lower case so it is un-exported (and only private to that package). In Golang to export any field from one pkg to another it should me in Upper case.
Two solutions:
1) Declare Map fields in Upper case, eg:
type MustKey struct {
// m map[string]string
// Upper case fields can be export to other packages
M map[string]string
}
2) Wrap your structure in one function and export the function name.
MustKey.m is an unexported field. You are attempting to initialize that field without referring to it by name with pkg.MustKey{map[string]string{"Hello": "bar"}}.
You either have to export the field by renaming it to M, or you have to define a constructor function that will set it in the package:
func NewMustKey(m map[string]string) MustKey {
return MustKey{m:m}
}
The field m in the MustKey struct is lower case. Therefore it is an unexported field and cannot be used by a program that imports the pkg package. Unexported fields have to be operated on by methods or functions that are internal to the pkg package. Or change it to an M and then use that externally.
You are implicitly using m when you do the initialization in main.
They seem to be the same:
package main
import "fmt"
type S struct {
i int
}
func main() {
var s1 *S = new(S)
fmt.Println(s1)
var s2 *S = &S{}
fmt.Println(s2) // Prints the same thing.
}
Update:
Hm. I just realized that there's no obvious way to initialize S.i using new. Is there a way to do that? new(S{i:1}) does not seem to work :/
From Effective Go:
As a limiting case, if a composite literal contains no fields at all, it creates a zero value for the type. The expressions new(File) and &File{} are equivalent.
Not only do they give the same resulting value, but if we allocate something both ways and look at their values...
// Adapted from http://tour.golang.org/#30
package main
import "fmt"
type Vertex struct {
X, Y int
}
func main() {
v := &Vertex{}
v2 := new(Vertex)
fmt.Printf("%p %p", v, v2)
}
...we'll see that they are in fact allocated in consecutive memory slots. Typical output: 0x10328100 0x10328108. I'm not sure if this is an implementation detail or part of the specification, but it does demonstrate that they're both being allocated from the same pool.
Play around with the code here.
As for initializing with new, according to the language spec: The built-in function new takes a type T and returns a value of type *T. The memory [pointed to] is initialized as described in the section on initial values. Because functions in go can't be overloaded, and this isn't a variadic function, there's no way to pass in any initialization data. Instead, go will initialize it with whatever version of 0 makes sense for the type and any member fields, as appropriate.
Case 1: package main
import (
"fmt"
)
type Drink struct {
Name string
Flavour string
}
func main() {
a := new(Drink)
a.Name = "Maaza"
a.Flavour = "Mango"
b := a
fmt.Println(&a)
fmt.Println(&b)
b.Name = "Frooti"
fmt.Println(a.Name)
}//This will output Frooti for a.Name, even though the addresses for a and b are different.
Case 2:
package main
import (
"fmt"
)
type Drink struct {
Name string
Flavour string
}
func main() {
a := Drink{
Name: "Maaza",
Flavour: "Mango",
}
b := a
fmt.Println(&a)
fmt.Println(&b)
b.Name = "Froti"
fmt.Println(a.Name)
}//This will output Maaza for a.Name. To get Frooti in this case assign b:=&a.