Introduction
Reading values with reflection is straightforward, but modifying them requires understanding addressability. A reflected value can only be set if it points to the original variable's memory. This is one of the most common sources of confusion (and panics) when working with reflection.
Key Concepts
- Addressability: A reflect.Value is settable only if it was obtained from a pointer.
reflect.ValueOf(x)copies x;reflect.ValueOf(&x).Elem()points to x. - CanSet(): Returns true if the value can be modified. Always check this before calling Set methods.
- Elem(): Dereferences a pointer or interface Value to get the underlying value.
Real World Context
You are building a configuration loader that reads environment variables and sets struct fields by name. Each field must be settable, which means you must pass a pointer to the struct. Understanding addressability is what makes this work.
Deep Dive
This code panics because reflect.ValueOf(x) creates a copy:
gox := 1 v := reflect.ValueOf(x) v.SetInt(2) // PANIC: unaddressable value
The fix is to pass a pointer and call Elem():
gox := 1 v := reflect.ValueOf(&x).Elem() // Get addressable value v.SetInt(2) // Works! fmt.Println(x) // 2
Always check settability before modifying:
gov := reflect.ValueOf(x) if v.CanSet() { v.SetInt(2) }
For structs, modify fields by name:
gotype User struct { Name string Age int } u := User{Name: "Alice", Age: 30} v := reflect.ValueOf(&u).Elem() v.FieldByName("Name").SetString("Bob") v.FieldByName("Age").SetInt(25)
Only exported fields can be modified via reflection. Attempting to set an unexported field panics.
Common Pitfalls
- Forgetting to pass a pointer —
reflect.ValueOf(x)copies x, making the Value unaddressable. Always usereflect.ValueOf(&x).Elem(). - Setting unexported fields — This panics. Only exported (capitalized) fields are settable via reflection.
Best Practices
- Always check CanSet() — Before calling any Set method, verify the value is settable to avoid panics.
- Check IsValid() for FieldByName —
FieldByName("nonexistent")returns an invalid Value. Check.IsValid()before using it.
Summary
reflect.ValueOf(x)copies x and is not settable. Usereflect.ValueOf(&x).Elem()instead.- Always check
CanSet()before modifying values. - Only exported struct fields can be modified via reflection.
- Use
FieldByName()to access struct fields dynamically, checkingIsValid()first.
Code Examples
// setField sets a struct field by name using reflection.
// The obj parameter must be a pointer to a struct.
func setField(obj any, name string, value any) error {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr {
return errors.New("must pass pointer")
}
field := v.Elem().FieldByName(name)
if !field.IsValid() {
return fmt.Errorf("no field %s", name)
}
if !field.CanSet() {
return fmt.Errorf("cannot set %s (unexported?)", name)
}
field.Set(reflect.ValueOf(value))
return nil
}
// Usage: setField(&user, "Name", "Bob")