Go Slice Copying: What Really Happens Under the Hood?
In Go, variables are passed by value, which means that when we pass variables as arguments to functions, Go makes a copy of those values for the function to use. However, the behaviour of Slice can be a bit confusing at first glance, especially if we don't know how it works under the hood.
Example 1: Modifying a Slice Without Affecting the Original
To understand this better, let's look at the first example. In this example, we have a function called AppendNumber
which appends the number 7 to a given slice. However, even though we modify the slice within the function call, we do not affect the original slice because Go creates a copy of the arguments for the function to work with.
func AppendNumber(slice []int) {
slice = append(slice, 7)
}
func main() {
slice := make([]int, 3, 6)
for i := 0; i < len(slice); i++ {
slice[i] = i
}
fmt.Println("before", slice) // Print 0 1 2
AppendNumber(slice)
fmt.Println("after", slice) // Print 0 1 2
}
The AppendNumber
function only modifies the copy of the slice within its scope, not the original slice, which is very straightforward.
Example 2: Modifying an Element in Slice
Now let's look at the second example. In this example, we have a function called UpdateNumber
which updates an element in the slice at index 1 to the value 7. Unlike the previous example, this time we notice that the original slice is modified.
func UpdateNumber(slice []int) {
slice[1] = 7
}
func main() {
slice := make([]int, 3, 6)
for i := 0; i < len(slice); i++ {
slice[i] = i
}
fmt.Println("before", slice) // Print 0 1 2
UpdateNumber(slice)
fmt.Println("after", slice) // Print 0 7 2
}
In the main function, we create a slice, assign values to it, and then print the slice before calling the UpdateNumber
function. The output shows the original values of the slice, which are 0, 1, and 2. After calling the UpdateNumber
function, we print the slice again, and this time the value at index 1 has changed to 7.
Knowing What Values Get Copied in Go
It seems that we still can influence the original Slice given the result of the second example. To think this through, we would need to know exactly what values are copied to the function arguments in Go. When a slice is passed to a function, the slice header (which contains a pointer to the underlying array, its length and capacity) is copied to the function arguments. The underlying array is not copied.
The slice header that is copied to the argument in the examples will look like this:
type sliceHeader struct {
Data uintptr // A pointer to the underlying array
Length int // The number of elements it contains
Capacity int // The maximum number of elements it can hold
}
In the first example, when we call the AppendNumber
function, a copy of the slice header is created so that any changes made to the slice within the function do not affect the original slice.
In the second example, when we call the UpdateNumber
function, a copy of the slice header is created. However, since the underlying array is not copied, both the original slice and the copy point to the same array in memory. Therefore any changes made to the slice (changing the value at index 1) will be reflected in both the original slice and the copy.
Conclusion
To conclude, modifying elements of a slice within a function will affect the original slice if the underlying array is shared between the original slice and the function's copy. If a new slice is created within the function (e.g. using append
), the original slice is not affected as we have updated the copy of the slice header to the newly created slice.
By understanding what values get copied to arguments in Go, and how changes to the slice header and array elements can affect the original slice, we can write more reliable and maintainable code. If you are interested in more information about Slice in Go, this article is worth a read: https://go.dev/blog/slices
I hope you have found this explanation of slices in Go useful and informative. If you have any further questions or need additional clarification, please feel free to ask.
Thanks for reading!
Subscribe to my newsletter
Read articles from Ray Yang directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ray Yang
Ray Yang
BUIDL | Dev | Loving to code