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?
CodePudding user response:
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
.
CodePudding user response:
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.