Assigning to type definition using reflection in Go - go

I have setup a type called Provider that defines an integer:
type Provider int
func (enum *Provider) Scan(raw interface{}) error {
*enum = Provider(int(raw))
}
If I create an object, Foo, with a Provider field, like this:
type Foo struct {
Code Provider
Value string
}
foo := &Foo {
Code: Provider(0),
Value: "derp",
}
var scnr sql.Scanner
scannerType := reflect.TypeOf(&scnr).Elem()
tType := reflect.TypeOf(foo)
tField := tType.Field(0)
fmt.Printf("Field %s, of type %s, kind %s\n",
tField.Name, tField.Type, tField.Type.Kind())
When I run this code, I get that the field is of type Provider and its Kind is int. However, I cannot assign an int to a Provider using reflection because this will fail:
getInteger() interface{} {
return 1
}
fValue := reflect.ValueOf(foo).Elem()
vField := fValue.Field(0)
vField.Set(reflect.ValueOf(getInteger())) // panic: reflect.Set: value of type int is not assignable to type Provider
The normal solution to this would be to do reflect.ValueOf(Provider(getInteger().(int))), however, this won't work because vField is set inside of a loop that iterates over a structure's fields and therefore will have a different type. Essentially, I would like a way to detect that vField is a definition of int (ie. Provider) rather than an int, itself; so I could use reflection to cast the integer value to Provider when I set it.
Is there a way I can make this work?

The kind and type are not identical terms. Kind of both int and Provider is int because Provider has int as its underlying type. But the type Provider is not identical to int. What you have is a type definition, but not a type alias! Those are 2 different things. See Confused about behavior of type aliases for details.
The type of Foo.Code field is Provider, you can only assign a value of type Provider to it:
vField.Set(reflect.ValueOf(Provider(1)))
This works.
Note that reflection or not, you can't assign int to Foo.Code:
var i int = 3
var foo Foo
foo.Code = i // Error!
The above has compile time error:
error: cannot use i (variable of type int) as type Provider in assignment
What may be confusing is that using a constant works:
var foo Foo
foo.Code = 3 // Works!
This is because 3 is an untyped constant representable by a value of type Provider, so the constant will be converted to Provider.

I ended up using the Convert function in conjunction with the Assignable function to do my check:
// Get value to assign and its type
vValue := reflect.ValueOf(getInteger())
tValue := vValue.Type()
// Check if the field can be assigned the value; if it can then do so
// Otherwise, check if a conversion can be assigned
vType := vField.Type()
if tValue.AssignableTo(vType) {
vField.Set(vValue)
} else if vValue.CanConvert(vType) {
vField.Set(vValue.Convert(vType))
}
This fixed my issue.

Related

Unable to assign untyped int to generic struct field [duplicate]

This question already has answers here:
Assigning a value literal to a struct field of a generic type without running into an IncompatibleAssign error
(1 answer)
Convert a type (int, float etc) to `T` [go1.18]
(2 answers)
How to declare and use a struct field which can store both string and int values?
(4 answers)
Closed 4 months ago.
Given a generic struct:
type R2[IDTYPE comparable] struct {
ID IDTYPE
IsActive bool
}
Implementing an interface:
type Storable interface {
Store(ctx context.Context) error
}
I would expect the following definition to work:
func (r R2[int]) Store(ctx context.Context) error {
r.ID = 123 // not allowed
// ...
return nil
}
However, the method definition is not allowed. The error is:
'123' (type untyped int) cannot be represented by the type IDTYPE (int)
Is it not yet possible to do this kind of generic field assignment in Go?
Addendum:
On go playground the error is:
cannot use 123 (untyped int constant) as int value in assignment
And converting to int(123) does not work. The error in this case is:
cannot use comparable(123) (untyped int constant 123) as int value in assignment
Instantiation must happen at the type level, not on a method level, and methods can't introduce new type parameters, see How to create generic method in Go? (method must have no type parameters)
This means when you want to use R2, you then have to choose type arguments for the type parameters, and the methods can't change those, you're "stuck" with the types you choose on R2's instantiation.
Also note that since the constraint for IDTYPE is comparable, which may be string for example, the integer 123 cannot be assigned to the ID field in all cases because it may have a type of string.
If you want / must handle multiple concrete types for the IDs, generics is not the right choice. Interfaces may be used instead:
type R2 struct {
ID any
IsActive bool
}
Also note that the receiver must be a pointer if you wish to modify the receiver (e.g. fields of a struct).
If you wish to restrict the values stored in ID to comparable, use a (generic) function for it.
Here's how you can do it:
type R2 struct {
ID any
IsActive bool
}
func (r *R2) Store(ctx context.Context) error {
setID(r, 123)
return nil
}
func setID[ID comparable](r *R2, id ID) {
r.ID = id
}
Testing it:
r := &R2{}
var s Storable = r
s.Store(context.TODO())
fmt.Println(r)
Which outputs (try it on the Go Playground):
&{123 false}
This provides flexibility (you can set any comparable values to the ID field using setID()), and provides compile-time safety: attempting to set an incomparable value will result in a compile-time error such as this:
setID(r, []int{1}) // Error: []int does not implement comparable

How to get a struct tag without having to instantiate the struct

type User struct{
Name string `json:"name"`
}
func main() {
t := reflect.TypeOf(User{})
}
It is possible to capture the tag's values ​​without instantiating it. For example capturing the value of the tag "json" without having to instantiate the User structure.
Creating a value is not a performance issue in almost all scenarios, but you can avoid creating a value by using a nil pointer value.
t := reflect.TypeOf((*User)(nil)).Elem() // Elem "dereferences" the pointer type.
The reason to use the pointer pattern shown here is that the one syntax works with every type. In particular, it handles interface types:
type I interface{}
// Naive developers might expect the following
// to return the type for I, but it actually
// returns nil.
t := reflect.TypeOf(I(nil))
// The pointer pattern returns the type for I.
t = reflect.TypeOf((*I)(nil)).Elem()
The pointer syntax is the same for all types independent of how the zero value is written for the type:
t = reflect.TypeOf(User{})
t = reflect.TypeOf(uint(0))
t = reflect.TypeOf(myString(""))
Use t = reflect.TypeOf((*T)(nil)).Elem() where T is any type:
t = reflect.TypeOf((*User)(nil)).Elem()
t = reflect.TypeOf((*uint)(nil)).Elem()
t = reflect.TypeOf((*myString)(nil)).Elem()

Set value of interface to struct field of type pointer to struc via reflection

I'm trying to set struct.field = &otherStruct. However, I have to use reflection, and otherStruct is of type interface{}.
The error I'm getting is:
reflect.Set: value of type main.StructB is not assignable to type *main.StructB
struct is known. The (real) type of otherStruct is not known, but it is guaranteed, that the assignment is safe (the struct type is identical).
Code:
type StrucA struct {
Field *StrucB
}
type StrucB struct {}
func main() {
a := StrucA{}
var b interface{} = StrucB{}
//above is set
// Target: Set a.Field = &b
reflect.ValueOf(&a).Elem().FieldByName("Field").Set(reflect.ValueOf(b)) // reflect.Set: value of type main.StrucB is not assignable to type *main.StrucB
}
Playground:
https://play.golang.org/p/LR_RgfBzsxa
I have tested a lot of different stuff, but I'm unable to solve it.
You first need to allocate a pointer to the type of b, in order to have somewhere to copy the value. Once you have a pointer value, you can set that to Field in a:
field := reflect.New(reflect.TypeOf(b))
field.Elem().Set(reflect.ValueOf(b))
reflect.ValueOf(&a).Elem().FieldByName("Field").Set(field)
https://play.golang.org/p/6-GNSEq0tw3

How to set default values in Go structs

There are multiple answers/techniques to the below question:
How to set default values to golang structs?
How to initialize structs in golang
I have a couple of answers but further discussion is required.
One possible idea is to write separate constructor function
//Something is the structure we work with
type Something struct {
Text string
DefaultText string
}
// NewSomething create new instance of Something
func NewSomething(text string) Something {
something := Something{}
something.Text = text
something.DefaultText = "default text"
return something
}
Force a method to get the struct (the constructor way).
From this post:
A good design is to make your type unexported, but provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. Also return an interface type and not a concrete type, and the interface should contain everything others want to do with your value. And your concrete type must implement that interface of course.
This can be done by simply making the type itself unexported. You can export the function NewSomething and even the fields Text and DefaultText, but just don't export the struct type something.
Another way to customize it for you own module is by using a Config struct to set default values (Option 5 in the link). Not a good way though.
One problem with option 1 in answer from
Victor Zamanian is that if the type isn't exported then users of your package can't declare it as the type for function parameters etc. One way around this would be to export an interface instead of the struct e.g.
package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
Name string
Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
return candidate{name, 0} // enforce the default value here
}
Which lets us declare function parameter types using the exported Candidate interface.
The only disadvantage I can see from this solution is that all our methods need to be declared in the interface definition, but you could argue that that is good practice anyway.
There is a way of doing this with tags, which
allows for multiple defaults.
Assume you have the following struct, with 2 default
tags default0 and default1.
type A struct {
I int `default0:"3" default1:"42"`
S string `default0:"Some String..." default1:"Some Other String..."`
}
Now it's possible to Set the defaults.
func main() {
ptr := &A{}
Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...
Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}
Here's the complete program in a playground.
If you're interested in a more complex example, say with
slices and maps, then, take a look at creasty/defaultse
From https://golang.org/doc/effective_go.html#composite_literals:
Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from package os.
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
What about making something like this:
// Card is the structure we work with
type Card struct {
Html js.Value
DefaultText string `default:"html"` // this only works with strings
}
// Init is the main function that initiate the structure, and return it
func (c Card) Init() Card {
c.Html = Document.Call("createElement", "div")
return c
}
Then call it as:
c := new(Card).Init()
I found this thread very helpful and educational. The other answers already provide good guidance, but I wanted to summarize my takeaways with an easy to reference (i.e. copy-paste) approach:
package main
import (
"fmt"
)
// Define an interface that is exported by your package.
type Foo interface {
GetValue() string // A function that'll return the value initialized with a default.
SetValue(v string) // A function that can update the default value.
}
// Define a struct type that is not exported by your package.
type foo struct {
value string
}
// A factory method to initialize an instance of `foo`,
// the unexported struct, with a default value.
func NewFoo() Foo {
return &foo{
value: "I am the DEFAULT value.",
}
}
// Implementation of the interface's `GetValue`
// for struct `foo`.
func (f *foo) GetValue() string {
return f.value
}
// Implementation of the interface's `SetValue`
// for struct `foo`.
func (f *foo) SetValue(v string) {
f.value = v
}
func main() {
f := NewFoo()
fmt.Printf("value: `%s`\n", f.GetValue())
f.SetValue("I am the UPDATED value.")
fmt.Printf("value: `%s`\n", f.GetValue())
}
One way to do that is:
// declare a type
type A struct {
Filed1 string
Field2 map[string]interface{}
}
So whenever you need a new variable of your custom defined type just call the NewA function also you can parameterise the function to optionally assign the values to the struct fields
func NewA() *A {
return &A{
Filed1: "",
Field2: make(map[string]interface{}),
}
}
for set default values in Go structs we use anonymous struct:
Person := struct {
name string
age int
city string
}{
name: "Peter",
age: 21,
city: "Noida",
}
fmt.Println(Person)
Structs
An easy way to make this program better is to use a struct. A struct is a type which contains named fields. For example we could represent a Circle like this:
type Circle struct {
x float64
y float64
r float64
}
The type keyword introduces a new type. It's followed by the name of the type (Circle), the keyword struct to indicate that we are defining a struct type and a list of fields inside of curly braces. Each field has a name and a type. Like with functions we can collapse fields that have the same type:
type Circle struct {
x, y, r float64
}
Initialization
We can create an instance of our new Circle type in a variety of ways:
var c Circle
Like with other data types, this will create a local Circle variable that is by default set to zero. For a struct zero means each of the fields is set to their corresponding zero value (0 for ints, 0.0 for floats, "" for strings, nil for pointers, …) We can also use the new function:
c := new(Circle)
This allocates memory for all the fields, sets each of them to their zero value and returns a pointer. (*Circle) More often we want to give each of the fields a value. We can do this in two ways. Like this:
c := Circle{x: 0, y: 0, r: 5}
Or we can leave off the field names if we know the order they were defined:
c := Circle{0, 0, 5}
type Config struct {
AWSRegion string `default:"us-west-2"`
}

convert function type in Golang

// Each type have Error() string method.
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
// type error interface {
// Error() string
// }
func (f binFunc) Error() string {
return "binFunc error"
}
func func_type_convert() {
var err error
err = binFunc(add)
fmt.Println(err)
fmt.Println(i)
}
I have two questions about the code above:
I don't know why the Error method executed, when add function was converted into binFunc type?
Why the add function converted result was able to assign to an err error interface variable?
error is an interface:
type error interface {
Error() string
}
This means that any type which has a method: Error() string fulfills the interface and can be assigned to a variable of type error.
binFunc has such a method:
func (f binFunc) Error() string {
return "binFunc error"
}
New developers in Go sometimes find this confusing because they don't realize it's possible to attach methods to more than just structs. In this case binFunc is defined liked this:
type binFunc func(int, int) int
So the way this works is you are allowed to convert any function which has the same signature: (from the spec)
A function type denotes the set of all functions with the same parameter and result types.
So if you create a function add:
func add(x, y int) int {
return x + y
}
You are allowed to convert this into a binFunc:
binFunc(add)
And because of the Error method on binFunc we defined above, we are then able to assign this new binFunc to a variable of type error:
var err error
var bf binFunc = binFunc(add)
err = bf
fmt.Println's behavior is to call .Error() on errors for you:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
So to answer your questions:
The Error method is executed because fmt.Println looks for arguments of type error, invokes .Error() and prints the resulting string.
You are allowed to assign binFuncs to err because binFunc has an Error method. You cannot assign add directly to err because it does not have an Error method. But you are allowed to convert add to a binFunc because they have the same function signature, and by doing so you can then assign it to the err variable.
go spec dependencies:
type casting or conversion -> assignability -> type identity
explicit type casting or conversion
binFunc and func(int, int) int have same underlying representation.
binFunc(add)
note, type casting can happen between 2 types that have the same underlying representation. However, their type can be totally different.
type MyInt int
func main() {
var b MyInt = 3
a := int(b)
fmt.Println(a, b)
}
variable assignment
check type identity
based on type identity rule, binFunc is identical to func(int, int) int. So you can do type casting as below:
A named type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types.
func(int, int) int
is type literal, and it's unnamed.
type binFunc func(int, int) int
is a named type.
Predeclared types, defined types, and type parameters are called named types. An alias denotes a named type if the type given in the alias declaration is a named type.
named type is different from others. but here, binFunc is compared with the un-named type: their underlying type literals are structurally equivalent, both func(int, int) int.
var bfunc binFunc = add
check assignability
variable of named type can be assigned with a value of unnamed type providing their underlying type is identical.
You may call this an implicit type conversion here, but it's not accurate. At least golang doesn't call this type conversion because the underlying type/representation is the same.
inspired by this answer
extra words
type assertion only requires type identity. Therefore, the rule is simpler than type assignability.

Resources