In this tutorial, we will walk through some examples of deep copy and shallow copy different data types in Golang. As it turns out, deep copy is the default for some data types, while shallow copy is the default for others.
shallow copy
: A copy of a data structure which shares any linked structures with the original. If you modify that, you'll modify all the shallow copies of the header that points to it.
deep copy
: A copy of a data structure duplicating not only the structure itself, but all structures to which it is linked
Copy basic data types in GO
In the below example, we will try to copy and change the value of basic data types such as float64, int64 and string:
package main
import "fmt"
func main() {
testInt := 64
testFloat := 12.5
testStr := "Test string"
testInt2 := testInt
testFloat2 := testFloat
testStr2 := testStr
fmt.Println("Before change:")
fmt.Println("Original variables")
fmt.Printf("int64: %v, float: %v, string: %v\n", testInt, testFloat, testStr)
fmt.Println("Copy variables")
fmt.Printf("int64: %v, float: %v, string: %v\n", testInt2, testFloat2, testStr2)
testInt2 = 105
testFloat2 = 63
testStr2 = "Copy string"
fmt.Println("After change:")
fmt.Println("Original variables")
fmt.Printf("int64: %v, float: %v, string: %v\n", testInt, testFloat, testStr)
fmt.Println("Copy variables")
fmt.Printf("int64: %v, float: %v, string: %v", testInt2, testFloat2, testStr2)
}
Output:
Before change:
Original variables
int64: 64, float: 12.5, string: Test string
Copy variables
int64: 64, float: 12.5, string: Test string
After change:
Original variables
int64: 64, float: 12.5, string: Test string
Copy variables
int64: 105, float: 63, string: Copy string
Copying basic data type does a copy by value. So modifying the destination, doesn’t modify the source and versa.Â
Copy reference types (pointer, slice, map,..)
- A pointer in Go is a variable that stores the memory address instead of value. The memory address can be of another value located in the computer.
- Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array.
- Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
The below example show how to copy slice and map in Go:
package main
import "fmt"
func main() {
testSlice := []int{1, 3, 4, 5, 6, 7, 1}
testMap := map[string]int{"Red": 102, "Black": 253}
copySlice := testSlice
copyMap := testMap
fmt.Println("Before change:")
fmt.Println("Original variables")
fmt.Printf("slice: %v, map: %v\n", testSlice, testMap)
fmt.Println("Copy variables")
fmt.Printf("slice: %v, map: %v\n", copySlice, copyMap)
copySlice[0] = 10000000
copyMap["Green"] = 1588963
fmt.Println("After change:")
fmt.Println("Original variables")
fmt.Printf("slice: %v, map: %v\n", testSlice, testMap)
fmt.Println("Copy variables")
fmt.Printf("slice: %v, map: %v\n", copySlice, copyMap)
}
Output:
Before change:
Original variables
slice: [1 3 4 5 6 7 1], map: map[Black:253 Red:102]
Copy variables
slice: [1 3 4 5 6 7 1], map: map[Black:253 Red:102]
After change:
Original variables
slice: [10000000 3 4 5 6 7 1], map: map[Black:253 Green:1588963 Red:102]
Copy variables
slice: [10000000 3 4 5 6 7 1], map: map[Black:253 Green:1588963 Red:102]
Copying map, slice,.. does a copy by reference. So modifying the destination is equal to modify the source and versa.Â
Copy struct in Golang
Perform deep copy of a struct
Here is an example of deep copying a struct to another a variable. All the fields in the struct are primitive data types:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
person1 := Person{"Anna", 23}
person2 := person1
fmt.Println("Before change:")
fmt.Println("Original variables")
fmt.Printf("person1: %v\n", person1)
fmt.Println("Copy variables")
fmt.Printf("person2: %v\n", person2)
person2.Name = "Bob"
fmt.Println("After change:")
fmt.Println("Original variables")
fmt.Printf("person1: %v\n", person1)
fmt.Println("Copy variables")
fmt.Printf("person2: %v\n", person2)
}
Output:
Before change:
Original variables
person1: {Anna 23}
Copy variables
person2: {Anna 23}
After change:
Original variables
person1: {Anna 23}
Copy variables
person2: {Bob 23}
Perform shallow copy of a struct
Here is an example of shallow a struct to another a variable. One of the field is reference type (a slice)
package main
import "fmt"
type Person struct {
Name string
Age int
Friends []string
}
func main() {
person1 := Person{"Anna", 23, []string{"Bob", "Teddy", "Wilson"}}
person2 := person1
person3 := &person1
fmt.Println("Before change:")
fmt.Println("Original variables")
fmt.Printf("person1: %v, person2: %v, person3: %v\n", person1, person2, person3)
fmt.Println("After change:")
person2.Friends = append(person2.Friends, "Kelly")
fmt.Printf("person1: %v, person2: %v, person3: %v\n", person1, person2, person3)
person3.Friends = append(person3.Friends, "Kelly")
fmt.Printf("person1: %v, person2: %v, person3: %v\n", person1, person2, person3)
}
Output:
Before change:
Original variables
person1: {Anna 23 [Bob Teddy Wilson]}, person2: {Anna 23 [Bob Teddy Wilson]}, person3: &{Anna 23 [Bob Teddy Wilson]}
After change:
person1: {Anna 23 [Bob Teddy Wilson]}, person2: {Anna 23 [Bob Teddy Wilson Kelly]}, person3: &{Anna 23 [Bob Teddy Wilson]}
person1: {Anna 23 [Bob Teddy Wilson Kelly]}, person2: {Anna 23 [Bob Teddy Wilson Kelly]}, person3: &{Anna 23 [Bob Teddy Wilson Kelly]}
Note that:
person3 := &person1
For a pointer
 referencing a struct
, we simply use an assignment operator to create a shallow copy.
Summary
In this tutorial, we have illustrate how to deep and shallow copy in Golang. Copying basic data type does a copy by value while copying map, slice,.. does a copy by reference. We learned how to copy struct using deep and shallow copy method.
References
https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
https://en.wiktionary.org/wiki/shallow_copy
https://go.dev/doc/effective_go
The difference between deep copy and shallow copy is reflected in the copy behavior of reference type variables, but the struct in the section “Perform deep copy of a struct” does not have reference type fields, if any, copying by assignment is still shallow copy
In a shallow copy, object B points to object A’s address in memory. In deep copy, all things in object A’s memory address get copied to object B’s memory address.
You are right: a struct that contains references cannot be deep copied using a built-in function but by default, Go creates a deep copy of structs with fields of primitive types. In the example shown above, you can see 2 structs have different addresses:
Code:
Output: