Copy different data types in GO [shallow & deep copy]

Copy different data types in GO [shallow & deep copy]

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.

gif

deep copy: A copy of a data structure duplicating not only the structure itself, but all structures to which it is linked

gif


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 apointerreferencing astruct, 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

Tuan Nguyen

Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms.