Primitive types
suggest changeLet’s see what kind of operations we can do on primitive types like int or string.
Get the type
func printType(v interface{}) {
rv := reflect.ValueOf(v)
typ := rv.Type()
typeName := ""
switch rv.Kind() {
case reflect.Ptr:
typeName = "pointer"
case reflect.Int:
typeName = "int"
case reflect.Int32:
typeName = "int32"
case reflect.String:
typeName = "string"
// ... handle more cases
default:
typeName = "unrecognized type"
}
fmt.Printf("v is of type '%s'. Size: %d bytes\n", typeName, typ.Size())
}
printType(int32(3))
printType("")
i := 3
printType(&i) // *int i.e. pointer to int
v is of type 'int32'. Size: 4 bytes
v is of type 'string'. Size: 16 bytes
v is of type 'pointer'. Size: 8 bytes
In real code you would handle all the types you care about.
Get the value
func getIntValue(v interface{}) {
var reflectValue = reflect.ValueOf(v)
n := reflectValue.Int()
fmt.Printf("Int value is: %d\n", n)
}
getIntValue(3)
getIntValue(int8(4))
getIntValue("")
Int value is: 3
Int value is: 4
panic: reflect: call of reflect.Value.Int on string Value
goroutine 1 [running]:
reflect.Value.Int(...)
/usr/local/go/src/reflect/value.go:986
main.getIntValue(0x4a01e0, 0x4db310)
/tmp/src442530047/main.go:14 +0x204
main.main()
/tmp/src442530047/main.go:24 +0x7b
exit status 2
To minimize API surface, Int() returns int64 and works on all signed integer values (int8, int16, int32, int64).
UInt() methods returns uint64 and works on every unsigned integer values (uint8, uint16, uint32, uint64).
Trying to get integer value from value of incompatible type (like string) will panic.
To avoid panic you can first check the type with Kind().
All methods for retrieving the value:
Bool() boolInt() int64UInt() uint64Float() float64String() stringBytes() []byte
Set the value
type S struct {
N int
}
func setIntPtr() {
var n int = 2
reflect.ValueOf(&n).Elem().SetInt(4)
fmt.Printf("setIntPtr: n=%d\n", n)
}
func setStructFieldDirect() {
var s S
reflect.ValueOf(&s.N).Elem().SetInt(5)
fmt.Printf("setStructFieldDirect: n=%d\n", s.N)
}
func setStructPtrField() {
var s S
reflect.ValueOf(&s).Elem().Field(0).SetInt(6)
fmt.Printf("setStructPtrField: s.N: %d\n", s.N)
}
func handlePanic(funcName string) {
if msg := recover(); msg != nil {
fmt.Printf("%s panicked with '%s'\n", funcName, msg)
}
}
func setStructField() {
defer handlePanic("setStructField")
var s S
reflect.ValueOf(s).Elem().Field(0).SetInt(4)
fmt.Printf("s.N: %d\n", s.N)
}
func setInt() {
defer handlePanic("setInt")
var n int = 2
rv := reflect.ValueOf(n)
rv.Elem().SetInt(4)
}
func setIntPtrWithString() {
defer handlePanic("setIntPtrWithString")
var n int = 2
reflect.ValueOf(&n).Elem().SetString("8")
}
setIntPtr: n=4
setStructFieldDirect: n=5
setStructPtrField: s.N: 6
setInt panicked with 'reflect: call of reflect.Value.Elem on int Value'
setStructField panicked with 'reflect: call of reflect.Value.Elem on struct Value'
setIntPtrWithString panicked with 'reflect: call of reflect.Value.SetString on int Value'
As setInt and setStructField show, you can only change values if you start with a pointer to the value.
Since reflect.ValueOf() creates a reflect.Value that represents a pointer to a value, you need to use Elem() to get reflect.Value that represents the value itself. You can then call SetInt() to set the value.
setStructPtrField shows how we can grab a reference to field value by it’s position in the struct.
Trying to set value of incompatible type will panic.
Methods that set values mirror those that read the values:
SetBool(v bool)SetInt(v int64)SetUInt(v uint64)SetFloat(v float64)SetString(v string)SetBytes(v []byte)