Skip to content

Reflection

Reflection lets a program examine its own types and values at runtime. It’s how packages like encoding/json can serialize arbitrary structs without you writing marshaling code by hand — but it’s slower and less type-safe than regular code, so reach for it only when generics or interfaces genuinely can’t do the job.

v := 42
t := reflect.TypeOf(v) // int
val := reflect.ValueOf(v)
fmt.Println(t.Kind()) // int
fmt.Println(val.Int()) // 42

This is exactly how encoding/json decides field names:

type User struct {
Name string `json:"name"`
}
t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("json")) // "name"

Mutating through reflection requires an addressable value, obtained via a pointer:

v := reflect.ValueOf(&u).Elem() // Elem() dereferences the pointer
v.FieldByName("Age").SetInt(31)

Calling Set* on a non-addressable value panics — this is one of the sharper edges of the reflect API.

  • Prefer generics for container/algorithm code — they’re checked at compile time and faster at runtime.
  • Prefer interfaces for polymorphism — implicit satisfaction already gives you most of what people reach for reflection to do.
  • Reflection-heavy code is harder to read, harder for tools to refactor safely, and meaningfully slower in hot paths — profile before assuming you need it.