Go标准库没有reflect.Copy函数,因反射拷贝需手动遍历字段、检查可设置性、处理类型兼容性及嵌套结构,不能简单调用单个函数。

为什么 reflect.Copy 不存在?别被名字骗了
Go 标准库没有 reflect.Copy 这个函数,这是初学者最容易卡住的第一步。反射做属性拷贝不是调一个函数就完事,得手动遍历字段、判断可设置性、处理类型兼容性。直接拿两个 struct 变量用 = 赋值只能用于同类型且字段名/顺序/导出性完全一致的情况;跨类型或部分字段映射必须靠反射驱动。
用 reflect.Value.Set() 前必须检查可设置性(CanSet())
反射对象默认是只读副本,即使源值可寻址,通过 reflect.ValueOf(x) 拿到的也是不可设置的。必须显式传入指针并调用 Elem() 才能获得可设置的 Value。常见错误是:对非指针目标调 Set(),程序 panic 报错 reflect.Value.Set using unaddressable value。
实操要点:
- 目标变量必须传指针:
reflect.ValueOf(&dst).Elem() - 源字段值需用
Convert()转为目标字段类型,否则Set()会 panic - 跳过未导出字段(
Field(i).CanInterface() == false)和不可设置字段(CanSet() == false) - 注意嵌套结构体:需递归处理,不能只看顶层字段
json.Marshal/Unmarshal 是最简但有隐含限制的替代方案
如果只是临时需要字段级浅拷贝,且源/目标字段名一致、类型可 JSON 序列化,用 json.Marshal + json.Unmarshal 最省事。但它不处理私有字段、会丢掉方法和 channel/func 等非 JSON 类型字段,且性能比纯反射低 3–5 倍。
典型写法:
srcBytes, _ := json.Marshal(src) json.Unmarshal(srcBytes, &dst)
注意点:
- 字段必须是导出的(首字母大写),否则 JSON 不识别
- 目标结构体字段标签如
json:"name"会影响映射,需与源匹配或统一忽略 - 时间类型
time.Time默认转成 RFC3339 字符串,反序列化时需确保目标字段也是time.Time
字段类型不匹配时,reflect.Convert() 的安全边界在哪?
反射中把 int 赋给 int64、string 赋给 []byte 是允许的,但 float64 → int 会 panic,string → int 不行,自定义类型之间也不能靠 Convert 自动转换。必须提前判断源字段类型是否在目标字段类型的合法转换集合内。
建议做法:
- 用
srcVal.Type().ConvertibleTo(dstField.Type())兜底判断,返回 false 就跳过或报错 - 对数字类型,可额外支持
int→int32、float64→float32等常见窄化,但要加范围检查(避免溢出) - 对
string和[]byte,手动做[]byte(srcStr)或string(srcBytes)转换,别依赖 Convert - 遇到 interface{} 字段,先用
srcVal.Interface()拿出原始值,再根据 dstField.Type() 判断能否 set
真正麻烦的是带泛型或嵌套 map/slice 的结构——这时候反射逻辑就得按类型分叉,没法靠一套循环走到底。字段名相同但嵌套层级不同、tag 映射规则不一致,这些细节往往比反射本身更耗调试时间。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











