How to concatenate two or more slices in Go

The builtin append() function eases the task of joining multiple slices in Go, but you need to watch out for certain side effects

Slice concatenation in Go is easily achieved by leveraging the built-in append() function. It takes a slice (s1) as its first argument, and all the elements from a second slice (s2) as its second. An updated slice with all the elements from s1 and s2 is returned which may be assigned to a different variable.

Here’s an example:

// https://play.golang.org/p/HYrE-fUCMmc
package main

import "fmt"

func main() {
	s1 := []string{"James", "Wagner", "Christene", "Mike"}
	s2 := []string{"Paul", "Haaland", "Patrick"}

	s3 := append(s1, s2...)
	fmt.Println(s3) // [James Wagner Christene Mike Paul Haaland Patrick]
}

The three dots operator (...) after the second argument is required because append() is a variadic function that accepts an unlimited number of arguments. The following line:

s3 := append(s1, s2...)

is basically a shorthand syntax for manually inputting each element in s2 into the append() function as follows:

s3 := append(s1, "Paul", "Haaland", "Patrick")

Side effects

It is important to point out that the append() function does not always create a new underlying array for the returned slice. If the capacity of s1 is big enough to accomodate the elements from s2, s3 will share the same underlying array as s1 which could have unintended side effects in your code.

// https://play.golang.org/p/IWh2vJgFx3v
package main

import "fmt"

func main() {
	s1 := make([]int, 2, 5)
	s1[0], s1[1] = 2, 3
	s2 := []int{7, 8}
	s3 := append(s1, s2...)
	fmt.Println(s1, s3) // [2 3] [2 3 7 8]
	s3[0] = 5
	fmt.Println(s1, s3) // [5 3] [5 3 7 8]
}

Notice how changing the value of an element in s3 causes s1 to change as well. This happens because they both share the same underlying array. To avoid this, you need to make sure that the resulting slice is backed by a new underlying array regardless of the capacity of s1. Here’s how:

// https://play.golang.org/p/UF2MjKOk_3n
package main

import "fmt"

func main() {
	s1 := make([]int, 2, 5)
	s1[0], s1[1] = 2, 3
	s2 := []int{7, 8}
	s3 := append(s1[:len(s1):len(s1)], s2...)
	fmt.Println(s1, s3) // [2 3] [2 3 7 8]
	s3[0] = 5
	fmt.Println(s1, s3) // [2 3] [5 3 7 8]
}

The trick is to ensure that the capacity of the first argument to append is the same as its length. This can be done by using a full slice expression which allows you to specify the maximum capacity of a slice.

The syntax of a full slice expression is as follows:

a[low:high:max]

If low is omitted, it defaults to 0:

a[:high:max]
// is equivalent to
a[0:high:max]

The maximum capacity of the slice is equal to max - low. To make it equivalent to the slice length, high and max need to have the same value as shown below:

s3 := append(s1[:len(s1):len(s1)], s2...)

Wrap up

In this article, we discussed how to concatenate two or more slices in Go, and also considered a side effect of using the append() function for this purpose as well as its remedy. If you have any further insights regarding slice concatenation in Go, feel free to leave a comment below.

Thanks for reading, and happy coding!