必须传指针:反射加载配置前需传结构体指针(如&Config{}),调用reflect.ValueOf(ptr).Elem()获取可寻址值,否则Set操作无效且CanSet()返回false;字段必须导出(大写开头),非导出字段无法访问;嵌套指针和切片需手动初始化,避免nil panic。

反射加载配置前必须传指针,否则字段赋值无效
Go 反射无法修改原值,只能通过指针间接写入。如果你传入 Config{} 而不是 &Config{},reflect.ValueOf() 拿到的是副本,后续所有 SetXxx() 都在改副本,原结构体字段仍是零值——运行时没报错,但配置根本没生效。
- 必须用
reflect.ValueOf(ptr).Elem()获取可寻址的字段值,否则CanSet()返回 false - 常见错误是直接对结构体字面量调用,比如
LoadConfig(Config{}, cfgMap),这会导致静默失败 - 函数签名应强制要求
interface{}且内部校验是否为指针:if reflect.TypeOf(v).Kind() != reflect.Ptr,不满足就 panic
字段必须导出,小写开头字段会被反射跳过
Go 反射不能访问非导出字段(首字母小写),哪怕 YAML/JSON 键名完全匹配,字段也始终是零值。这不是 bug,是语言安全机制,但容易被忽略——配置文件写了 port: 8080,结构体却定义成 port int `yaml:"port"`,结果 port 始终为 0,且无任何提示。
- 所有待映射字段必须大写开头,例如
Port int `yaml:"port"` - 可在加载逻辑里加字段检查:
if !f.IsExported()就提前报错,避免上线后排查半天 - 别依赖“自动转换”,
gopkg.in/yaml.v3不会把dbHost自动转成db_host,标签必须显式声明
嵌套结构体和指针字段要手动初始化,否则 nil panic
YAML 中某个嵌套块缺失(比如没写 database:),而结构体字段是 *DatabaseConfig 类型,反射不会自动 new 它——直接 Field(i).Set() 会 panic:assignment to entry in nil map 或 invalid memory address。
- 遍历字段前,先判断是否为指针类型且当前值为 nil:
if f.Kind() == reflect.Ptr && !f.IsNil() - 如果是 nil 指针,用
f.Set(reflect.New(f.Type().Elem()))初始化 - 切片字段同理,空 slice(
[]string)可直接 Set;nil slice(nil)需先reflect.MakeSlice()
选 gopkg.in/yaml.v3,别用 v2 或 github.com/go-yaml/yaml
gopkg.in/yaml.v2 对空值、锚点、嵌套结构支持弱,遇到 null 字段常静默跳过或 panic;github.com/go-yaml/yaml 是 v3 镜像,但 import 路径不统一,容易引发版本冲突或 tag 解析异常。
立即学习“go语言免费学习笔记(深入)”;
- 固定用
go get gopkg.in/yaml.v3,import 写死为gopkg.in/yaml.v3 - v3 默认把 YAML 中的
null映射为 Go 零值(0、""、false),更符合反射赋值预期 - 时间字段如
expires: 2025-04-01T00:00:00Z,v3 默认不解析 RFC3339,得让结构体字段类型为time.Time并实现UnmarshalYAML方法


















