Essential Go Reflection  Edit on GitHub      File Issue

Primitive types

Let’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(0x10a0360, 0x10d32b0, 0x98, 0x10d32b0)
	/usr/local/Cellar/go/1.10/libexec/src/reflect/value.go:931 +0x127
main.getIntValue(0x10a0360, 0x10d32b0)
	/books/go/0400-reflection/get_value.go:12 +0x5b
main.main()
	/books/go/0400-reflection/get_value.go:22 +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:

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:

  ↑ ↓ to navigate     ↵ to select     Esc to close