Go > Collections > Arrays and Slices > Copying slices
Copying Slices in Go
Learn how to effectively copy slices in Go using the copy
function. Understand the difference between shallow and deep copies and their implications for your code. This guide provides practical examples and best practices for handling slice copies to avoid common pitfalls.
Basic Slice Copying using copy
This code demonstrates the fundamental way to copy a slice in Go using the built-in copy
function. First, we initialize a sourceSlice
with some integer values. Then, we create a destinationSlice
using make
. The crucial part is that the destinationSlice
*must* be initialized with sufficient capacity to hold the copied elements. The copy
function then iterates through the sourceSlice
, copying elements into the destinationSlice
until either the sourceSlice
is exhausted or the destinationSlice
is full. The function returns the number of elements actually copied.
package main
import "fmt"
func main() {
// Original slice
sourceSlice := []int{1, 2, 3, 4, 5}
// Create a destination slice with the same length as the source
destinationSlice := make([]int, len(sourceSlice))
// Copy elements from source to destination
numCopied := copy(destinationSlice, sourceSlice)
fmt.Println("Source Slice:", sourceSlice)
fmt.Println("Destination Slice:", destinationSlice)
fmt.Println("Number of elements copied:", numCopied)
}
Concepts Behind the Snippet
Slices in Go are descriptors of array segments. They don't own the underlying data. When you assign one slice to another (slice2 := slice1
), you're just copying the slice header (pointer to the underlying array, length, and capacity), not the array itself. This creates a shallow copy. The copy
function, on the other hand, copies the *elements* from one slice to another. If the elements are primitive types (int, string, bool), this effectively creates a deep copy of those elements. However, if the slice contains pointers or complex types, the copy
function still only copies the pointers, resulting in a shallow copy of the referenced data.
Real-Life Use Case
Imagine you're building a data processing pipeline where you need to manipulate a slice of data without affecting the original. For example, you might be filtering or transforming data. Using copy
allows you to work on a separate copy, ensuring the integrity of your original data. Another use case is in concurrent programming. When multiple goroutines need to access and modify a slice, copying the slice allows each goroutine to work on its own copy, preventing race conditions and data corruption.
Best Practices
copy
function will only copy as many elements as the destination can hold.copy
doesn't return an error, always check the returned value to understand how many elements were actually copied. This is crucial when the destination slice has a smaller capacity than the source.
Interview Tip
Be prepared to explain the difference between assigning a slice to another variable (shallow copy) and using the copy
function. Also, be able to discuss the implications of shallow vs. deep copies, particularly when dealing with slices of pointers or complex types. A good way to showcase understanding is to explain how modifications to one shallow copy affect the other.
When to use them
Use copy
when you need to create a distinct, independent copy of a slice's elements. This is crucial when you want to modify the copy without affecting the original slice. If you just need another reference to the same underlying data, a simple assignment is sufficient.
Memory Footprint
Using copy
increases memory usage because you're creating a new slice and copying the elements. The impact depends on the size of the slice and the type of elements. Slices of primitive types will have a smaller memory footprint than slices of complex objects. Consider the memory implications, especially when dealing with large datasets. Shallow copies are more memory-efficient but come with the risk of unintended side effects.
Alternatives
copy
.
Pros
copy
function is easy to use and understand.
Cons
copy
.
Copying Slices of Structs
This example extends the basic copying concept to slices of structs. Because structs are value types in Go, the copy
function creates a deep copy of the struct elements themselves. Modifying the Name
field in the peopleCopy
slice does not affect the original people
slice. This is because each Person
struct in the copy is a completely independent copy of the corresponding struct in the original.
package main
import "fmt"
// Define a struct
type Person struct {
Name string
Age int
}
func main() {
// Original slice of structs
people := []Person{
{"Alice", 30},
{"Bob", 25},
}
// Create a destination slice
peopleCopy := make([]Person, len(people))
// Copy the slice
copy(peopleCopy, people)
// Modify the copy
peopleCopy[0].Name = "Charlie"
fmt.Println("Original:", people)
fmt.Println("Copy:", peopleCopy)
// Check if the original is affected (it shouldn't be)
if people[0].Name == "Alice" {
fmt.Println("Original slice is not affected.")
} else {
fmt.Println("Original slice is affected.")
}
}
FAQ
-
What happens if the destination slice has a smaller capacity than the source slice?
Thecopy
function will only copy as many elements as the destination slice can hold. It returns the number of elements actually copied, which will be less than the length of the source slice. -
Does
copy
create a deep copy of the slice?
Thecopy
function creates a shallow copy of the slice header (pointer, length, capacity) but copies the *elements* themselves. For primitive types (int, string, bool), this effectively creates a deep copy of the data. For slices of pointers or complex types, it copies the pointers, resulting in a shallow copy of the referenced data. To deep copy slices of complex types, you'll need to iterate and manually copy each element. -
Is it necessary to use the
copy
function to copy a slice?
No, you can also achieve the same effect by iterating through the slice and appending each element to a new slice. However, using the built-incopy
function is generally more efficient.