最常用且推荐的方式是append(dst, src...),需确保类型一致并带...操作符;预分配容量可避免多次扩容;copy适用于精确控制内存布局;类型不一致时需显式转换。
直接用 append + 展开操作符是最常用且推荐的方式
只要两个 slice 类型一致(比如都是 []int),append(dst, src...) 就是标准解法。注意必须带 ...,否则编译报错:cannot use src (type []t) as type t in argument to append。
常见错误写法:append(dst, src)(漏掉 ...)或 append(dst, src1, src2)(传多个切片,不合法)。
正确示例:
slice1 := []int{1, 2}
slice2 := []int{3, 4}
result := append(slice1, slice2...)
- 返回新切片,原
slice1不变(除非容量足够,底层复用数组) - 若
slice1容量不足,会分配新底层数组并拷贝全部数据 - 简单、清晰、符合 Go 惯例,90% 场景够用
合并多个 slice 时,预分配容量能显著减少内存拷贝
连续调用 append(如 append(append(dst, a...), b...))在 dst 容量不足时可能触发多次扩容,每次扩容都伴随一次底层数组拷贝。对大 slice 或高频合并场景,性能损耗明显。
更稳的做法是先算总长,再 make 一次分配:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
totalLen := len(a) + len(b) + len(c) dst := make([]int, 0, totalLen) dst = append(dst, a...) dst = append(dst, b...) dst = append(dst, c...)
- 只发生一次底层数组分配(假设容量没超限)
- 避免中间状态切片的冗余拷贝
- 尤其适合已知各 slice 长度、且合并频次高的逻辑(如日志批量 flush、网络包拼接)
用 copy 手动拼接适用于需完全控制内存布局的场景
当你要确保结果切片与输入切片完全无关(比如防止底层数组意外共享),或已有目标缓冲区(如复用池中的预分配 slice),copy 更直接:
dst := make([]int, len(a)+len(b)) copy(dst, a) copy(dst[len(a):], b)
- 不依赖
append的扩容逻辑,行为可预测 - dst 必须提前分配好长度(不是容量),
copy不会扩容 - 如果 dst 长度不够,
copy只复制 min(len(dst), len(src)) 个元素,无声截断——容易漏掉数据 - 不适合动态增长场景,仅用于“已知最终大小 + 精确控制”
类型不一致时不能硬拼,得手动转换
Go 是强类型语言,[]int 和 []string 之间无法合并;[]interface{} 也不能直接接收 []int —— 即使你写 append([]interface{}{}, intSlice...),也会编译失败,因为展开后每个元素是 int,而目标期望 interface{}。
真要混合类型,只能显式转换:
dst := make([]interface{}, 0, len(ints)+len(strs))
for _, v := range ints { dst = append(dst, interface{}(v)) }
for _, v := range strs { dst = append(dst, interface{}(v)) }
- 别指望
reflect.Append来绕过类型检查,它既不安全又慢,还容易 panic - 如果只是临时传参给接受
...interface{}的函数(如fmt.Println),直接展开原 slice 即可:fmt.Println(ints...) - 长期存混合数据,建议定义具体 struct,而非滥用
interface{}
最易被忽略的是底层数组共享问题:用 append 合并时,若原切片容量足够,新切片仍指向旧数组。如果其他变量还引用着同一底层数组,修改新切片可能意外影响它们——这不是 bug,是设计使然,但必须心里有数。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










