返回结构体指针应优先用 &t{...} 字面量构造,避免 new(t) 后手动赋值;含不可拷贝字段必须返回指针;小结构体按值返回更符合惯例;非 nil 指针应确保已初始化;大对象或含切片/映射时才考虑指针以减少拷贝;error 必须最后返回且出错时其他值为零值。

返回结构体指针时,别手动 new() 再赋值
直接用 &T{...} 字面量构造并取地址,比先 new(T) 再逐字段赋值更清晰、更安全。Go 的逃逸分析会自动把这种字面量分配到堆上,无需担心栈变量地址问题。
常见错误是写成:
func NewUser(name string) *User {
u := new(User) // ❌ 所有字段为零值,易漏初始化
u.Name = name
return u
}
应改为:
func NewUser(name string) *User {
return &User{Name: name} // ✅ 一行完成,字段明确,逃逸由编译器保证
}
- 如果结构体含不可拷贝字段(如
sync.Mutex),必须返回指针,否则编译失败 - 小结构体(如
type Point {X, Y int})按值返回更符合 Go 惯例,没必要加指针 - 命名建议统一用
NewXXX前缀,调用方一眼可知返回的是指针实例
返回指针前,必须确保不返回 nil
除非语义明确需要“空”状态(如 FindUser 查不到时返回 nil),否则函数应返回已初始化的指针。否则调用方解引用时会 panic。
反例:
func CreateUser(name string) *User {
if name == "" {
return nil // ⚠️ 调用方可能直接 *u 得到 panic
}
return &User{Name: name}
}
推荐写法:
func CreateUser(name string) *User {
if name == "" {
return &User{Name: "anonymous"} // ✅ 非 nil,调用方无需额外判空
}
return &User{Name: name}
}
- 若确实需表达“未找到”,应配合文档说明,并让调用方显式检查
if u != nil - 避免在函数内部构造后忘记赋值字段,比如
&User{Age: 25}中Name为""是合法零值,不是 bug,但可能是逻辑疏漏 - 对 map、slice 等类型,返回其指针要格外小心:修改
*map会影响原始数据,这不是拷贝隔离
大对象返回值,优先用指针而非值
结构体字段多、含大数组或嵌套切片时(例如超过 16 字节),按值返回会触发完整内存拷贝,性能损耗明显。此时返回 *T 仅传地址,开销固定。
典型场景包括:
- 加载配置(
type Config struct { Rules []Rule; Schema map[string]any }) - 缓存实例(含大量预计算字段的结构体)
- 网络响应结构体(含原始 payload 字节流)
注意:不要为了“看起来高效”而滥用指针。比如 func GetID() *int 返回单个整数指针,反而增加 GC 压力和间接访问成本,直接返回 int 更合适。
指针返回值 + error 处理,error 必须放最后且非 nil 时其他返回值应为零值
Go 错误处理规范要求:error 作为最后一个返回值;出错时,所有非-error 返回值应为对应类型的零值(nil、0、"" 等),否则调用方无法安全使用。
正确示例:
func LoadUser(id int) (*User, error) {
if id
<p>错误写法:</p>
<pre class="brush:php;toolbar:false;">func LoadUser(id int) (*User, error) {
if id
<p>这个细节容易被忽略:一旦 error 非 nil,意味着操作失败,其余返回值不应携带任何有效业务状态——哪怕它“看起来能用”。</p>golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











