Concatenating Slices in Go: From Beginner to Pro in 2025
Slices in Go are dynamic arrays that are the heart of most Go applications. Sooner or later, every developer faces the task: "How to combine two slices into one?". It seems like a trivial operation, but, like many things in Go, it has its pitfalls and performance nuances.
On Stack Overflow, this question has garnered thousands of views, and for good reason: suboptimal concatenation can lead to excessive memory allocations and reduced application speed. In this article, we will cover all the legitimate ways to join two slices, from the classic append to the new standard library additions of 2025, and determine which method to choose depending on the situation.
Why doesn't simple addition work?
Unlike Python (list1 + list2) or JavaScript (arr1.concat(arr2)), Go does not support the + operator for slices. This was done intentionally: Go is a language that requires explicitness. Every operation with slices must be transparent, especially regarding memory management.
Let's get straight to the point. Here is the simplest and most common way:
package main
import "fmt"
func main() { slice1 := []int{1, 2, 3} slice2 := []int{4, 5, 6}
// Method #1: append with unpacking result := append(slice1, slice2...) fmt.Println(result) // [1 2 3 4 5 6]}This code works, but it has a nuance. If slice1 has enough capacity (cap), then append will modify it directly, rather than creating a new slice. This can lead to unexpected side effects if you did not anticipate the mutation of the original slice.
Safe concatenation using make + copy
If you need guaranteed independence of the result from the original slices, use the combination of make and copy. This is the "gold standard" for creating a new slice without the risk of modifying the original.
func concatSlices[T any](a, b []T) []T { result := make([]T, len(a)+len(b)) copy(result, a) copy(result[len(a):], b) return result}
// Usage:combined := concatSlices(slice1, slice2)Why is this good?
- Predictability: You always create a new slice. The original data remains untouched.
- Control: You explicitly allocate memory once, rather than relying on the internal logic of
append. - Generics (Go 1.18+): The function works with any data type.
Modern approach: slices.Concat (Go 1.22+)
Starting from Go version 1.22 (which by 2025 has long been the standard), the golang.org/x/exp/slices package introduced the Concat function. In Go 1.22, it was moved to the standard library.
This is the official, recommended way to concatenate, combining the safety of copy with the conciseness of append.
import "slices"
func main() { a := []string{"Go", "Python", "Rust"} b := []string{"JavaScript", "TypeScript"}
combined := slices.Concat(a, b) // combined = ["Go", "Python", "Rust", "JavaScript", "TypeScript"]}Advantages of slices.Concat:
- Readability: The name speaks for itself. The code becomes declarative.
- Optimization: Internally, it uses efficient allocation and
copy. - Flexibility: You can pass any number of slices:
slices.Concat(s1, s2, s3, s4).
If you are writing code on Go 1.22+ (and in 2025 this is mainstream), feel free to use slices.Concat. It is the best choice for 90% of tasks.
When is append still needed?
Classic