Skip to content

Instantly share code, notes, and snippets.

@lpar
Created April 20, 2016 18:08
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lpar/9e0431c1bc957a78cf3df90c8bf32617 to your computer and use it in GitHub Desktop.
Save lpar/9e0431c1bc957a78cf3df90c8bf32617 to your computer and use it in GitHub Desktop.
A detailed example of reflection in Go, interrogating and modifying field values in a struct
package main
import (
"fmt"
"reflect"
)
// A detailed example of examining a struct value in Go via reflection,
// and changing field values (if possible).
//
// See <http://lpar.ath0.com/2016/04/20/reflection-go-modifying-struct-values/>
// for accompanying article.
type Name string
type Person struct {
FirstName Name
LastName Name
Age int
}
func reflectOn(s interface{}) {
// Get the type and value of the argument we were passed.
ptyp := reflect.TypeOf(s)
pval := reflect.ValueOf(s)
// We can't do much with the Value (it's opaque), but we need it in order
// to fetch individual fields from the struct later.
var typ reflect.Type
var val reflect.Value
// If we were passed a pointer, dereference to get the type and value
// pointed at.
if ptyp.Kind() == reflect.Ptr {
fmt.Printf("Argument is a pointer, dereferencing.\n")
typ = ptyp.Elem()
val = pval.Elem()
} else {
fmt.Printf("Argument is %s.%s, a %s.\n", ptyp.PkgPath(), ptyp.Name(),
ptyp.Kind())
typ = ptyp
val = pval
}
// Make sure we now have a struct
if typ.Kind() != reflect.Struct {
fmt.Printf("Not a struct.\n")
return
}
// Can we set values?
if val.CanSet() {
fmt.Printf("We can set values.\n")
} else {
fmt.Printf("We cannot set values.\n")
}
// The number of fields in the struct is determined by the type of struct
// it is. Loop through them.
for i := 0; i < typ.NumField(); i++ {
// Get the type of the field from the type of the struct. For a struct,
// you always get a StructField.
sfld := typ.Field(i)
// Get the type of the StructField, which is the type actually stored
// in that field of the struct.
tfld := sfld.Type
// Get the Kind of that type, which will be the underlying base type
// used to define the type in question.
kind := tfld.Kind()
// Get the value of the field from the value of the struct.
vfld := val.Field(i)
// Dump out what we've found
fmt.Printf("struct field %d: name %s type %s kind %s value %v\n", i,
sfld.Name, tfld, kind, vfld)
// Is that field some kind of string, and is the value one we can set?
if kind == reflect.String && vfld.CanSet() {
fmt.Printf("Overwriting field %s\n", sfld.Name)
// Assign to it
vfld.SetString("Anonymous")
}
}
}
func main() {
t := Person{"John", "Smith", 23}
fmt.Printf("First, passing the actual structure:\n\n")
reflectOn(t)
fmt.Printf("After reflection:\n%s %s, %d years old\n",
t.FirstName, t.LastName, t.Age)
fmt.Printf("\nNow, passing a pointer to the structure:\n\n")
reflectOn(&t)
fmt.Printf("After reflection:\n%s %s, %d years old\n",
t.FirstName, t.LastName, t.Age)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment