Passing Nested Structures by Reference through Reflection
Traversing and modifying nested structures through reflection allows for dynamic manipulation of data. However, when encountering a struct field that is itself a struct, setting values using reflection can lead to a "reflect.Value.Set using unaddressable value" panic.
Understanding the Issue
The root of the problem lies in the way reflection handles nested structures. By default, structs are passed by value, which means when we retrieve the Value from a nested struct field for modification, we are operating on a copy of the data rather than the original. Any changes made affect only the copy, not the actual structure.
Solution: Passing by Reference
To set field values of a nested struct, it is necessary to pass it as a pointer. This can be achieved using the Value.Addr() method, which returns a pointer to the actual struct value.
Recursive Default Value Setting
The recursion in the sample code reflects the need to traverse all fields of a struct and its nested structs. To set default values, we call setDefaultValue on the pointer value of each field. For primitive types (int, string, bool), the default values are hardcoded. For nested structs, setDefaultValue is called recursively to handle their fields.
Working Example
Consider the following sample code:
<code class="go">type Client struct { Id int Age int PrimaryContact Contact Name string } type Contact struct { Id int ClientId int IsPrimary bool Email string } func SetDefault(s interface{}) error { return setDefaultValue(reflect.ValueOf(s)) } func setDefaultValue(v reflect.Value) error { if v.Kind() != reflect.Ptr { return errors.New("Not a pointer value") } v = reflect.Indirect(v) switch v.Kind() { case reflect.Int: v.SetInt(42) case reflect.String: v.SetString("Foo") case reflect.Bool: v.SetBool(true) case reflect.Struct: // Iterate over the struct fields for i := 0; i < v.NumField(); i++ { err := setDefaultValue(v.Field(i).Addr()) if err != nil { return err } } default: return errors.New("Unsupported kind: " + v.Kind().String()) } return nil } func main() { a := Client{} err := SetDefault(&a) if err != nil { fmt.Println("Error: ", err) } else { fmt.Printf("%+v\n", a) } }</code>
Output:
{Id:42 Age:42 PrimaryContact:{Id:42 ClientId:42 IsPrimary:true Email:Foo} Name:Foo}
This example showcases how to pass a nested structure by reference through reflection, setting default values using recursion.
The above is the detailed content of How to Pass Nested Structures by Reference in Reflection and Set Default Values Recursively?. For more information, please follow other related articles on the PHP Chinese website!