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


GO

Author: Tuan Nguyen
Reviewer: Deepak Prasad

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.

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

 

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

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

 

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

 

Tuan Nguyen

Tuan Nguyen

He is 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. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

2 thoughts on “Copy different data types in GO [shallow & deep copy]”

  1. 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

    Reply
    • 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:

      fmt.Printf("struct 1 address: %p\n", &person1)
      fmt.Printf("struct 2 address: %p\n", &person2)

      Output:

      struct 1 address: 0xc000092060
      struct 2 address: 0xc000092078
      Reply

Leave a Comment