scan方法必须接收interface{}是因为数据库驱动解包后传入的是指向原始值(如string、[]byte)的指针,而非结构体字段地址;若硬转为mystruct会panic,需通过类型断言和reflect安全提取值。

为什么 Scan 方法必须接收 *interface{} 而不是具体类型指针
Go 的 database/sql 在调用 Scanner.Scan 时,传入的是一个 *interface{}——它实际指向底层驱动解包后的原始值(比如 *string、*[]byte),而非你的结构体字段地址。如果你在 Scan 方法里试图直接解引用为 *MyStruct,会 panic:reflect: reflect.Value.Interface: cannot return value obtained from unexported field or method 或更常见的 sql: Scan error on column index 0: destination not a pointer。
实操建议:
- 始终在
Scan实现中先做类型断言:src, ok := src.(*interface{}),再解包*src获取真实值 - 用
reflect.ValueOf(*src).Elem().Interface()安全提取目标值,避免硬编码类型转换 - 对
nil输入(如数据库 NULL)要显式检查,否则reflect.Value.Elem()会 panic
自定义类型嵌套 JSON 字段时 Scan 和 Value 不对称的坑
常见场景:把 API 请求体 type User struct { Metadata map[string]interface{} } 存进 JSONB 字段,读取时想自动反序列化。问题在于:Value() 返回 []byte 没问题,但 Scan() 收到的可能是 *[]byte、*string,甚至 PostgreSQL 驱动返回的 *pgtype.JSONB(用 lib/pq 时则可能是 *[]byte)。
实操建议:
- 不要假设输入一定是
*[]byte;统一用reflect.Value处理各种底层类型 - 对
lib/pq,NULL 值传入的是nil,而pgx可能传&pgtype.JSONB{Status: pgtype.Null},需分别判断 -
Scan中反序列化失败时,别直接 return error —— 应设置字段为零值并记录日志,否则整行查询失败
实现 Scanner 接口时忘记导出字段导致反射失败
如果你的结构体字段是小写开头(如 type Config struct { data map[string]any }),即使实现了 Scan,在 Scan 内部用 reflect.Value.SetMapIndex 或类似操作时,会因字段未导出而静默失败或 panic:reflect: call of reflect.Value.SetMapIndex on map Value。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
实操建议:
- 所有需被
Scan修改的字段必须首字母大写(即 exported) - 如果不想暴露字段,用私有字段 + 公开 setter 方法,然后在
Scan里调用该方法(而不是直接反射赋值) - 用
go vet检查反射相关代码:它能发现部分「对未导出字段调用 Set」的问题
用 pgx 时 Scan 接收 pgtype 类型的注意事项
pgx 默认使用自己的类型系统,比如 JSONB、UUID、TimeTZ。当你为自定义类型实现 Scanner,且数据库字段是 JSONB,Scan 收到的很可能是一个 *pgtype.JSONB,而不是 *[]byte。直接类型断言 src.(*[]byte) 必然失败。
实操建议:
- 优先用
pgx自带的pgtype.Scanner接口组合,而不是只实现database/sql.Scanner - 若必须兼容多驱动,在
Scan中按顺序尝试断言:*[]byte→*string→*pgtype.JSONB→*pq.Jsonb - 测试时别只用
sql.Open("postgres", ...),要实际连pgxpool并执行查询,否则类型行为不一致
最麻烦的从来不是写 Scan,而是不同驱动对同一种 SQL 类型返回完全不同的 Go 类型——你得在运行时猜对方今天想给你什么。留个心眼,每加一个数据库字段类型,就补一条对应的类型分支和测试用例。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










