Go is statically typed. Reflection allows a program to inspect and manipulate its own types and variables at runtime.
The standard library uses it heavily for things like encoding/json and fmt.Printf.
reflect.TypeOf(x)gives you metadata: the name, the kind (Struct, String, Slice), and struct tags.reflect.ValueOf(x)gives you the actual runtime value, allowing you to read it or mutate it.
Reflection comes with three enormous penalties:
- Loss of Compile-Time Safety: If you try to call
v.SetString("hello")on anint, it will cause a structuralpanicat runtime, crashing your app. - Performance Devastation: Reflection defies compile-time optimizations like inlining. It is orders of magnitude slower than native code.
- Obscure Code: Heavy reflection creates code that is impossible to navigate with an IDE.
When to use it:
- Building generic decoders like
json.Unmarshal. - Validating structs dynamically via Struct Tags (
validate:"required").
When NOT to use it:
- If an
interfaceworks, use an interface. - If Generics (
[T any]) solve the problem mathematically at compile time, use Generics.
To change a value via reflection, you MUST pass a pointer to it.
v := reflect.ValueOf(&myStruct).Elem()
If you do not call .Elem() on a pointer, you cannot use .Set(), and the app will panic.
ex01_validator.goex02_copier.goex03_refactor.go