Go > Collections > Arrays and Slices > Slice internals and sharing
Understanding Slice Sharing in Go
This example demonstrates how slices in Go share the underlying array. Modifying a slice might affect other slices if they share the same array. It's crucial to understand this behavior to avoid unexpected side effects.
The Basic Concept: Slice Sharing
Slices in Go are not independent data structures. They are essentially views into an underlying array. Multiple slices can point to the same array. Therefore, changes made through one slice might be visible through another if they share the same underlying array. This sharing is a key aspect of slice efficiency, but it can also lead to subtle bugs if not understood properly.
Code Example: Demonstrating Shared Underlying Array
This code creates an array arr
and then creates two slices, slice1
and slice2
, that overlap. When we modify slice1[0]
, the change is also reflected in the original array arr
and in slice2
because they all point to the same underlying data.
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[0:3]
slice2 := arr[1:4]
fmt.Println("Original array:", arr)
fmt.Println("Slice 1:", slice1)
fmt.Println("Slice 2:", slice2)
slice1[0] = 100
fmt.Println("\nAfter modifying slice1:")
fmt.Println("Original array:", arr)
fmt.Println("Slice 1:", slice1)
fmt.Println("Slice 2:", slice2)
}
Explanation of the Output
The output of the program demonstrates that modifying slice1
alters the underlying array and subsequently affects slice2
. This is because slice1
and slice2
are backed by the same array. The key takeaway is that slice operations can have unexpected side effects if you are not aware of the sharing mechanism. Careful consideration should be given to whether copying the slice data is necessary to avoid unintended modifications.
Avoiding Unintended Modifications: Copying Slices
To avoid modifying the original array or other slices that share the same underlying array, you can create a new slice and copy the data. The copy
function creates a new slice with the same length and capacity as the source slice and copies the elements. Now, modifying slice1
will not affect slice2
because they have different underlying arrays.
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[0:3]
slice2 := make([]int, len(slice1))
copy(slice2, slice1)
fmt.Println("Original array:", arr)
fmt.Println("Slice 1:", slice1)
fmt.Println("Slice 2 (copied):", slice2)
slice1[0] = 100
fmt.Println("\nAfter modifying slice1:")
fmt.Println("Original array:", arr)
fmt.Println("Slice 1:", slice1)
fmt.Println("Slice 2 (copied):", slice2)
}
When to use them
Use slice sharing when you want to efficiently pass around portions of a large array without incurring the cost of copying the data. However, be extremely cautious when modifying shared slices, as changes can affect other parts of your code. Copy slices when you need to ensure that modifications to one slice do not affect others.
Memory footprint
Sharing slices reduces memory footprint because you're not duplicating the underlying data. Copying slices increases memory footprint because you're creating a new copy of the data.
Interview Tip
A common interview question is about slice sharing and how to avoid unintended side effects. Be prepared to explain the concept of underlying arrays, slice headers (pointer, length, capacity), and the use of the copy
function.
Best Practices
copy
function when you need to modify a slice without affecting the original data.
Real-Life Use Case Section
Image processing is a good example. Imagine processing a large image. You might use slices to represent different regions of the image. By passing slice to different image processing operations, without copy operation, you can increase performance.
FAQ
-
What is the difference between length and capacity of a slice?
The length of a slice is the number of elements currently present in the slice. The capacity is the maximum number of elements the slice can hold before it needs to be reallocated. The capacity is determined by the size of the underlying array from the slice's starting index to the end of array. When you append to a slice and its length reaches its capacity, a new, larger array is allocated, and the slice's underlying array is updated to point to the new array. This involves copying the existing elements to the new location. -
How can I create a slice that doesn't share its underlying array with another slice?
Use thecopy
function to create a new slice with its own underlying array. Alternatively, you can use themake
function to create a new slice with a specified length and capacity, which will allocate a new underlying array.