liveBook · Manning

Reflect.Value

As explained in Go in Practice, Second Edition

reflect.Value is a Go reflection value that represents the runtime value of a program object and exposes operations for inspecting it.

A `reflect.Value` represents a Go value for runtime inspection. Start with `reflect.ValueOf(x)`, then use `Kind()` to identify its underlying kind or `Type()` to obtain its specific type. A named integer type, for example, has its own type while retaining an integer kind. section 13.1 section 13.1.1

Sum by kind rather than type

Suppose `sum` accepts values through `interface{}`. A type switch with an `int64` case does not match a separately named type based on `int64`. Instead, obtain `ref := reflect.ValueOf(val)` and switch on `ref.Kind()`. The example reads signed integers with `ref.Int()`, an unsigned integer with `ref.Uint()`, and a string with `ref.String()` before parsing it. Use a type switch instead when the specific type matters. section 13.1.1

Listing 13.3 A kind switch
package main

import (
     "fmt"
     "reflect"
     "strconv"
)

type MyInt int64
func main() {
     var a uint8 = 2
     var b int = 37
     var c string = "3.2"
     var d MyInt = 1
     res := sum(a, b, c, d)
     fmt.Printf("Result: %f\n", res)
}

func sum(v ...interface{}) float64 {
     var res float64 = 0
     for _, val := range v {
            ref := reflect.ValueOf(val)   #1
            switch ref.Kind() {                    #2
            case reflect.Int, reflect.Int64:   #3
                   res += float64(ref.Int())     #4
            case reflect.Uint8:
                   res += float64(ref.Uint())   
            case reflect.String:
                   a, err := strconv.ParseFloat
                   (ref.String(), 64)                 
                   if err != nil {
                          panic(err)
                   }
                   res += a
            default:
                   fmt.Printf("Unsupported type %T. Ignoring.\n", val)
            }
     }
     return res
}

The Go listing shows `ValueOf`, `Kind`, `Int`, `Uint`, and `String` working together in the sum function. section 13.1.1

The same value inspection supports walking a struct at runtime. Begin with a value, use `reflect.Indirect(reflect.ValueOf(u))` to inspect through a pointer, and combine the resulting value with `reflect.Type` to report fields and recurse into nested structs. The walker prints a value’s type and kind alongside its field information. section 13.1.3

Listing 13.7 Recursively examining a value
func main() {
     fmt.Println("Walking a simple integer")
     var one MyInt = 1   #1
     walk(one, 0)        #1
     fmt.Println("Walking a simple struct")
     two := struct{ Name string }{"foo"}    #2
     walk(two, 0)                           #2
fmt.Println("Walking a struct with struct fields") #2
     p := &Person{                                       #3
            Name:    &Name{"Count", "Tyrone", "Rugen"
            },                                          
            Address: &Address{"Humperdink Castle", 
            "Florian"},                                   
     }                                                  
     walk(p, 0)                                         
}
type MyInt int
type Person struct {
     Name    *Name
     Address *Address
}
type Name struct {
     Title, First, Last string
}
type Address struct {
     Street, Region string
}
func walk(u interface{}, depth int) {            #4
     val := reflect.Indirect(reflect.ValueOf(u))   #5
     t := val.Type()   #6
     tabs := strings.Repeat("\t", depth+1)   #7
     fmt.Printf("%sValue is type %q (%s)\n", tabs, t, val.Kind())
     if val.Kind() == reflect.Struct {   #8
            for i := 0; i < t.NumField(); i++ {
                   field := t.Field(i)                  #9
                   fieldVal := reflect.Indirect
                   (val.Field(i))                            
                   tabs := strings.Repeat("\t", depth+2)
                   fmt.Printf("%sField %q is type %q (%s)\n",
                          tabs, field.Name, field.Type, fieldVal.Kind())
                   if fieldVal.Kind() == 
                   reflect.Struct {                             #10
                          walk(fieldVal.Interface(), depth+1)   #10
                   }                                            #10
            }
     }
}

The recursive walker places `reflect.Value` in a concrete struct-inspection program. section 13.1.3

How Go in Practice, Second Edition covers it

How deep it goes
Works through `ValueOf`, `Kind`, numeric and string accessors, and a recursive struct walker; does not address modifying values.
Examples
Uses a mixed-value sum with a named integer type and a walker for simple and nested structs.
What it assumes
Assumes Go variables, types, interfaces, and structs; defines the reflection distinction between values, types, and kinds.
What you can do after
Build it from scratch: write kind-based value handling and a runtime struct-inspection walker.
How it connects
Connects reflection values to `reflect.Type`, `reflect.Kind`, type switches, pointers, and struct fields.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go in Practice, Second Edition. The section links point to the parts of the book it draws on.