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.
reflect.TypeOf and reflect.ValueOf
Section titled “reflect.TypeOf and reflect.ValueOf”v := 42t := reflect.TypeOf(v) // intval := reflect.ValueOf(v)
fmt.Println(t.Kind()) // intfmt.Println(val.Int()) // 42Reading struct tags
Section titled “Reading struct tags”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"Setting values (with caveats)
Section titled “Setting values (with caveats)”Mutating through reflection requires an addressable value, obtained via a pointer:
v := reflect.ValueOf(&u).Elem() // Elem() dereferences the pointerv.FieldByName("Age").SetInt(31)Calling Set* on a non-addressable value panics — this is one of the sharper edges of the reflect API.
When to avoid it
Section titled “When to avoid it”- 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.