结构体传参选struct还是struct,只看三点:≤64字节且只读→传struct;需修改字段、含引用类型或>64字节→传struct;不确定先用*struct。

传值还是传指针?看这三点直接决策
不需要查文档、不靠猜——Go里结构体传参选 struct 还是 *struct,只看三个硬指标:
- 结构体大小 ≤ 64 字节(约 8 个
int64或 16 个int32)且函数只读 → 直接传struct - 函数要改字段、含
slice/map/chan字段、或结构体 > 64 字节 → 必须传*struct - 不确定?先写
*struct;后期压测发现无修改需求且调用极频繁,再切回值传递
别信“小结构体一定安全”——type Point struct{ X, Y int } 在每毫秒调用 10 万次的热路径上,累积拷贝仍会触发 CPU 缓存失效。用 go tool compile -S 看汇编里有没有 MOVQ 大块数据,比凭感觉准。
字段不可访问?90% 是导出问题
写 val.name 报错 undefined (type XXX has no field or method name),不是语法错,是 Go 的导出规则在拦你:
- 字段名首字母必须大写(
Name✅,name❌)才能被其他包访问 - 哪怕在同一文件,如果函数在另一个包(比如
main调utils包里的函数),小写字段也完全不可见 - 嵌套结构体字段也要逐层导出:
type User struct{ Profile *Profile }中,Profile的字段如Age也得大写
别用 interface{} + 类型断言绕过——那是为泛型兼容留的后门,不是解决导出问题的正路。导出字段,是最轻量、最安全的解法。
嵌套结构体传参时类型不匹配
把 Child 实例传给期待 Parent 参数的函数,报错 cannot use data (type Child) as type Parent,这不是 bug,是 Go 的类型系统在守门:
- 匿名嵌入(
type Child struct{ Parent })≠ 类型继承;Child和Parent是两个独立类型 - 想传过去?显式取嵌入字段:
myfunc(data.Parent),而不是myfunc(data) - 如果需要多态行为,定义接口(如
type Personer interface{ GetName() string }),让Child和Parent都实现它
强行用类型断言或反射绕过,只会把类型错误拖到运行时,且丧失编译器检查。Go 的“啰嗦”,恰恰是它少出线上事故的原因。
切片里装结构体,传参别犯迷糊
常见误写:func process(items []MyStruct) 然后在函数里疯狂改 items[i].Field = xxx——改的是副本,原切片没变:
-
[]MyStruct本身是 header(含指针、长度、容量),传值开销小,但其中每个MyStruct元素仍是值拷贝 - 要改元素字段?传
[]*MyStruct,或函数内用索引改原始底层数组:items[i].Field = xxx仅当MyStruct是小结构体且你确认不改字段才安全 - 更常用的是传
*[]MyStruct(极少)或直接传[]MyStruct+ 返回新切片(函数纯化)
真正容易被忽略的是:切片头传值没问题,但里面结构体字段改不改得动,取决于你传的是值还是指针——这层间接性,比单个结构体传参更容易踩空。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











