传[]t就足够了,绝大多数场景无需传[]t;仅当需让调用方感知切片头变化(如扩容后更新len/cap)时才考虑[]t或返回新切片。

直接说结论:传 []T 就够了,绝大多数场景下**不需要、也不该传 *[]T**;只有极少数需要“让调用方看到新切片头(len/cap/ptr)”的场景,才考虑用 **[]T 或返回新切片。
为什么传 []T 通常就足够了
切片本身是轻量结构体(ptr、len、cap 三个字段),值传递只复制这 24 字节,不拷贝底层数组。只要你想改元素内容(比如 s[i].Name = "x")、或用 append 且不关心扩容后是否影响原变量,[]T 完全胜任。
-
append(s, x)若未扩容,底层数组不变,原切片的ptr仍有效 —— 但你没把返回值赋回去,原变量的len不变 -
s[i].Method()调用指针方法时,&s[i]指向的是原底层数组里的真实元素,不是副本 - 传
*[]T反而强制逃逸:编译器会把整个切片头(24 字节)分配到堆上,增加 GC 压力
什么时候必须用 *[]T 或 **[]T
仅当你要在函数内修改切片头本身,并让调用方感知到变化 —— 比如扩容后要更新调用方持有的 len 或 cap,或者函数要“清空”原切片(*s = (*s)[:0])。
-
*[]T:能改len和cap,也能让append的新地址写回原变量(前提是扩容前容量足够,否则append返回新头,仍需手动赋值) -
**[]T:真正能处理任意扩容场景,因为你可以解引用两次:**s = append(*s, x),但极少需要,API 易用性差 - 更常见、更推荐的做法是:函数返回新切片,由调用方显式赋值,例如
s = grow(s)
结构体方法里怎么安全合并切片
如果你为自定义类型 type Points []Point 写 Merge 方法,关键看接收者类型:
- 值接收者
func (p Points) Merge(q Points):内部p = append(p, q...)只改副本,原变量不变 - 指针接收者
func (p *Points) Merge(q Points):必须写成*p = append(*p, q...),否则append(*p, q...)返回新切片但没存回去 - 如果只是查询(如
Contains),值接收者更安全、无额外开销
AWS SDK 等要求 []*T 时怎么转换
错误写法:params := []cloudformation.Parameter{...}; input.Parameters = params → 编译报错 cannot use params (type []cloudformation.Parameter) as type []*cloudformation.Parameter。
-
[]T和[]*T是完全不同的类型,Go 不允许隐式转换 - 正确做法:遍历原切片,对每个元素取地址,构造新切片:
ps := make([]*cloudformation.Parameter, len(params)); for i := range params { ps[i] = ¶ms[i] } - 别试图用
unsafe或反射绕过类型检查——SDK 接口明确要求指针切片,是为了避免内部深拷贝或支持 nil 元素语义
最常被忽略的一点:你写的 append 是否真被赋值回原变量?很多“没生效”的问题,根源不是指针传得不对,而是漏了那一行 s = append(s, x) 的赋值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











