Go 反射赋值需确保值可寻址且字段可导出:必须用 reflect.ValueOf(&v).Elem() 获取可寻址值,检查 field.CanSet() 和类型匹配后调用 Set* 方法,否则会 panic。
Go 语言本身不支持运行时动态类型赋值(如 Python 的 setattr),但 reflect 包提供了在运行时操作结构体字段的能力——前提是目标字段必须是**可寻址且可导出的**(即首字母大写)。直接对非导出字段或不可寻址值调用 Set* 会 panic。
reflect.Value.Set() 会 panic: reflect: reflect.Value.Set using unaddressable value这是最常遇到的错误。根本原因是:你传入的是一个值拷贝(reflect.ValueOf(structInstance)),而非其地址。
reflect.ValueOf(v) 返回的是 v 的副本,不可寻址,无法写入reflect.ValueOf(&v).Elem() 获取指向结构体的指针再解引用,才能得到可寻址的 reflect.Value
v 本身是 nil 指针,.Elem() 也会 panic核心步骤:获取可寻址的 reflect.Value → 定位字段 → 类型检查 → 调用对应 Set* 方法。
FieldByName 返回零值 Value)field.CanSet() == true,避免 panicSetString() 只接受 string;SetInt() 接受 int64,需手动转换FieldByName + Addr().Elem()
type User struct {
Name string
Age int
Active bool
}
u := User{}
v := reflect.ValueOf(&u).Elem() // ✅ 可寻址
nameField := v.FieldByName("Name")
if nameField.CanSet() && nameField.Kind() == reflect.String {
nameField.SetString("Alice")
}
ageField := v.FieldByName("Age")
if ageField.CanSet() && ageField.Kind() == reflect.Int {
ageField.SetInt(30)
}
这不是 reflect 原生支持的功能,需手动遍历 key 并映射到字段。注意大小写、tag(如 json:"user_name")、类型转换。
json 或自定义 tag),再 fallback 到字段名map[string]interface{} 中的数字默认是 float64,需按目标字段类型做类型断言和转换int vs int64)func FillStruct(dst interface{}, src map[string]interface{}) error {
v := reflect.ValueOf(dst)
if v.Kind() != reflect.Ptr || v.IsNil() {
return fmt.Errorf("dst must be non-nil pointer")
}
v = v.Elem()
if v.Kind() != reflect.Struct {
return fmt.Errorf("dst must point to struct")
}
for key, val := range src {
field := v.FieldByNameFunc(func(name string) bool {
tag := v.Type().FieldByName(name).Tag.Get("json")
if tag != "" && tag != "-" {
return strings.Split(tag, ",")[0] == key
}
return strin
gs.EqualFold(name, key) // case-insensitive fallback
})
if !field.IsValid() || !field.CanSet() {
continue
}
// 类型转换逻辑(此处简化为 string/int/bool)
switch field.Kind() {
case reflect.String:
if s, ok := val.(string); ok {
field.SetString(s)
}
case reflect.Int, reflect.Int64:
if f, ok := val.(float64); ok {
field.SetInt(int64(f))
}
case reflect.Bool:
if b, ok := val.(bool); ok {
field.SetBool(b)
}
}
}
return nil
}
真正难的不是调用 SetString,而是确保整个反射链路每一步都可寻址、可设置、类型兼容——尤其在泛型函数或深层嵌套场景下,漏掉一次 .Addr().Elem() 或没判 CanSet() 就会 crash。别依赖文档“应该可以”,每次写完都用边界 case(nil 指针、小写字段、类型不匹配)测一遍。