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() bool
Int() int64
UInt() uint64
Float() float64
String() string
Bytes() []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)