反射赋值前必须确保value可寻址且可设置,正确做法是传入指针并调用elem();结构体字段需导出且实例可寻址;setstring仅接受string类型;setfloat对float32会静默截断精度;嵌套指针字段需判空再创建实例;务必用canset()校验并处理nil指针。

反射赋值前必须确保 Value 可寻址且可设置
直接对 reflect.ValueOf(x) 调用 SetInt 会 panic,错误信息是 reflect.Value.SetInt using unaddressable value。这是因为传入的变量是值拷贝,Value 对象背后没有真实内存地址可写。
正确做法是传入指针并调用 Elem() 获取其指向的值:
num := 42 v := reflect.ValueOf(&num).Elem() // ✅ 可设置 v.SetInt(100)
- 如果原始变量是接口类型(如
interface{}),需先断言出具体类型再取地址,否则reflect.ValueOf(&iface).Elem()仍不可设 - 结构体字段默认不可设置,除非该字段是导出字段(首字母大写)且整个结构体实例本身可寻址
-
reflect.Value.CanSet()是唯一可靠判断方式,别依赖CanAddr()—— 后者为 true 不代表可设(例如切片底层数组元素)
SetString 只接受字符串类型,不自动类型转换
SetString 不是“把任意值转成字符串再设”,它只接受 string 类型的 reflect.Value,且目标必须是 string 类型的变量。常见误用是试图用它给 int 或 json.RawMessage 赋值,会直接 panic:reflect.Value.SetString using value obtained using unexported field 或类型不匹配错误。
- 给非 string 类型赋值,请用对应方法:整数用
SetInt,浮点用SetFloat,布尔用SetBool - 若输入是字符串但目标是数字类型,需手动解析(如
strconv.ParseInt),再用SetInt;反射不做隐式转换 - 对
[]byte赋字符串值?不行。得用SetBytes([]byte(str)),且目标必须是[]byte类型
SetFloat 对 float32/float64 的精度和类型敏感
SetFloat 接收 float64 参数,但目标如果是 float32 字段,它会自动截断精度——这不是报错,而是静默丢精度。容易在配置加载或序列化场景中引发数值偏差。
立即学习“go语言免费学习笔记(深入)”;
var f32 float32 = 0 v := reflect.ValueOf(&f32).Elem() v.SetFloat(3.1415926535) // ✅ 不 panic,但 f32 变成 3.1415927
- 检查目标类型是否为
float32:用v.Type() == reflect.TypeOf(float32(0)).Type() - 若需保持精度,应先用
float32(val)显式转换再传给SetFloat(注意:SetFloat内部仍按 float64 处理,但 float32 值会被安全收缩) - 对
interface{}中的 float 值做反射赋值时,务必确认底层具体类型,reflect.ValueOf(i).Kind()返回reflect.Float64并不代表它能安全设进float32字段
嵌套结构体或指针字段的 Set 操作容易空指针 panic
对结构体字段调用 Field(i).Set(x) 时,如果该字段是指针类型(如 *time.Time)且当前值为 nil,直接 Set 会 panic:reflect.Value.Set using unaddressable value —— 因为 nil 指针无法解引用。
- 安全写法:先判断字段是否为 nil 指针,若是,用
reflect.New(field.Type().Elem())创建新实例,再Set - 对嵌套结构体(如
A.B.C),每层都要检查可设性:v.FieldByName("B").IsValid() && v.FieldByName("B").CanInterface() - 使用
SetNil()清空指针字段没问题,但不能对非指针类型调用,否则 panic
CanSet() 校验和指针字段的 nil 判断——这两处一跳过,运行时 panic 几乎必然发生。










