Golang实现Scan/Value接口时传指针解决HugeParam报错问题
Golang结构体实现SQL序列化接口的hugeParam报错解决
问题现状
需要将Golang自定义结构体存储至SQL数据库,已按如下代码实现Scanner与Valuer接口的Scan、Value方法:
func (attr *myStruct) Scan(src interface{}) error { switch v := src.(type) { case string: return json.Unmarshal([]byte(v), attr) case []byte: return json.Unmarshal(v, attr) } return fmt.Errorf("cannot convert %T to My struct", src) } //nolint:hugeParam func (attr mystruct) Value() (driver.Value, error) { return json.Marshal(attr) }
存在两个问题:
- 结构体体积达320字节,lint检测提示
Value方法值接收者触发HugeParam报错 - 尝试添加nolint注解忽略报错未生效,执行
golangci-lint run --deadline 300s的相关报错信息如下:
level=warning msg="[runner] The linter 'interfacer' is deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner. " level=warning msg="[runner/nolint] Found unknown linters in //nolint directives: gocritic:hugeparam" internal/app/domain/entity/feedbacks.go:61:7: hugeParam: attr is heavy (320 bytes); consider passing it by pointer (gocritic)
其中interfacer相关警告为linter本身已废弃的提示,和当前代码问题无关,无需处理。
核心疑问为:是否支持将Value方法的接收者改为指针类型传参。
解决方案
1. 优先方案:将Value方法接收者改为指针类型
完全支持将Value方法的接收者改为指针类型,这也是从根源解决大结构体值拷贝性能问题、消除lint报错的最优方案。database/sql包的driver.Valuer接口本身不限制接收者类型,修改后的Value方法实现如下:
func (attr *myStruct) Value() (driver.Value, error) { return json.Marshal(attr) }
注意事项:
- 代码中向SQL操作传参时,需要传入结构体指针(即
&myStruct{}形式),值类型的结构体不会被识别为实现了driver.Valuer接口,会走默认反射序列化逻辑,可能出现不符合预期的结果 Scan方法本身就是指针接收者,修改后两个方法的接收者类型统一,不会出现接口实现不一致的问题
2. 修复nolint注解写法(不推荐,仅用于临时兼容)
之前注解不生效是因为写法错误:hugeParam是gocritic检查器下的具体检查项,不需要在注解中拼接gocritic:前缀,且注解和对应方法定义之间不能有空行。正确注解写法如下:
//nolint:hugeParam func (attr myStruct) Value() (driver.Value, error) { return json.Marshal(attr) }
如果需要忽略gocritic对该方法的所有检查,可将注解替换为//nolint:gocritic。
3. 根源优化(可选)
可以按照Go内存对齐规则调整结构体字段的定义顺序,缩小结构体整体内存占用,从根源避免大参数值拷贝的性能问题。
内容的提问来源于stack exchange,提问作者foc
相关产品推荐
相关产品推荐

