Reflection powers encoding/json, ORMs and validators; you need it to write such libraries and to understand them.
The reflect package lets a program examine values whose type isn't known at compile time. It starts from an interface value:
reflect.TypeOf(x)returns areflect.Type: itsName(),Kind()(Struct, Slice, Ptr, Int…), and for structsNumField()andField(i), which includes the field's name and tag (f.Tag.Get("json")).reflect.ValueOf(x)returns areflect.Valueholding the data, with accessors likeInt(),String(),Len(),Index(i),Field(i),MapRange().
Kind vs Type: a type UserID int has Type UserID but Kind Int. Generic reflection code switches on Kind.
To modify a value through reflection you need an addressable value: pass a pointer and call Elem(), as in reflect.ValueOf(&cfg).Elem(). v.CanSet() reports whether setting is allowed; unexported fields are readable but not settable. Calling SetInt on a non-settable value, or Int() on a non-integer Kind, panics — reflection trades compile-time checks for run-time panics.
The Laws of Reflection summarise it: reflection goes from interface value to reflection object and back (v.Interface()), and to modify a reflection object the value must be settable.
Costs: reflective code is slower (often 10–100× for field access), allocates, and is harder to read. Prefer interfaces, generics or code generation (go generate) when they fit, and keep reflection at the edges — serialisation, dependency injection, test helpers. Cache per-type information such as parsed tags, since inspecting a Type repeatedly is wasteful.
Example
package main
import (
"fmt"
"reflect"
)
type Config struct {
Host string `env:"HOST"`
Port int `env:"PORT"`
}
func main() {
cfg := Config{Host: "localhost"}
v := reflect.ValueOf(&cfg).Elem() // addressable struct
t := v.Type()
for i := range t.NumField() {
f := t.Field(i)
fmt.Println(f.Name, f.Type.Kind(), f.Tag.Get("env"))
}
// Host string HOST
// Port int PORT
if p := v.FieldByName("Port"); p.CanSet() {
p.SetInt(8080)
}
fmt.Println(cfg.Port) // 8080
}Remember
- TypeOf for type info (Kind, fields, tags), ValueOf for data
- To set via reflection, start from a pointer and call Elem()
- Reflection errors are run-time panics; keep it at the edges
Watch out for
- Calling reflect.ValueOf(cfg) (not &cfg) and then trying to set a field — it panics because the value isn't addressable.