You get a reflect.Value back from reflect.ValueOf and try to change it in place, the same way you'd assign to a normal variable. It compiles fine. It panics the instant it runs, because reflect.ValueOf handed you a read-only view of a copy — not a handle onto the original variable at all.
var x float64 = 3.4
v := reflect.ValueOf(x) // NOT a pointer -- v wraps a copy
v.SetFloat(7.1) // tries to mutate that copy
// panic: reflect: reflect.Value.SetFloat using unaddressable valueThe reflect package provides reflect.TypeOf and reflect.ValueOf to inspect the concrete type and value hidden behind an interface{} at runtime, and can go further to read struct tags, iterate fields, or even set values dynamically. It’s the mechanism behind generic-feeling standard library code like encoding/json and fmt.Println, which need to handle any type without knowing it in advance — but it bypasses compile-time type checking and runs noticeably slower than direct code, so idiomatic Go treats it as a tool of last resort for application code.
TypeOf and ValueOf
reflect.TypeOf returns a value's dynamic type; reflect.ValueOf returns a wrapper you can query and, for addressable values, mutate.
var x float64 = 3.4
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
fmt.Println(t) // float64
fmt.Println(v.Kind()) // float64
fmt.Println(v.Float()) // 3.4This is also the fix for the panic in the callout above: reflect.ValueOf(x) only lets you read, because x was passed by value — the reflect.Value has no way back to the original variable. To mutate it, you have to pass a pointer and call .Elem() to get an addressable value pointing at what the pointer points to:
var x float64 = 3.4
v := reflect.ValueOf(&x).Elem() // pointer, then Elem() -- addressable
v.SetFloat(7.1)
fmt.Println(x) // 7.1Inspecting struct fields and tags
This is reflection's most common real-world use: walking a struct's fields to read their names, types, and tags — exactly what encoding/json does to know which struct field maps to which JSON key.
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
t := reflect.TypeOf(User{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Println(field.Name, field.Tag.Get("json"))
}
// Name name
// Age ageWhy idiomatic Go avoids it when possible
Reflection code loses the compiler's help: a typo in a field name or a wrong type assertion becomes a runtime panic instead of a compile error, and reflective operations run substantially slower than direct field access or a type switch. Since generics arrived in Go 1.18, many use cases that once needed reflection (writing one function that works across several types) can be solved with type parameters instead, which keep compile-time safety. Reflection remains the right tool specifically for generic serialization, ORMs, and dependency-injection frameworks that must handle arbitrary, unknown-in-advance types.