deepcopy

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2022 License: MIT Imports: 0 Imported by: 2

README

DeepCopy

Go Reference Go Report Card Test GitHub release GitHub license

DeepCopy helps you create deep copies (clones) of your maps and slices.

The package is based on type assertions and does not use reflection.

Installation

Install DeepCopy with the go get command:

go get -u github.com/gohobby/deepcopy

How it works

DeepCopy returns a new object with all recursively duplicated children. This means that changes made to the original object will not affect the copied object and vice versa.

To copy a card or a slice:

m := map[string]interface{}{"foo": []string{"bar", "baz"}}
cloneMap := deepcopy.Map(m).DeepCopy() // interface{}

s := []interface{}{1, 2, &m}
cloneSlice := deepcopy.Slice(s).DeepCopy() // interface{}

You can also use the Clone function to get the copy directly into the expected type, for example:

m := map[string]interface{}{"foo": []string{"bar", "baz"}}
cloneMap := deepcopy.Map(m).CLone() // map[string]interface{}

s := []interface{}{1, 2, &m}
cloneSlice := deepcopy.Slice(s).Clone() // []interface{}

To copy your custom types, implement the Copyable interface and define your own deep copy function:

type Map map[string]interface{}

func (n Map) DeepCopy() interface{} {
	clone := make(Map, len(n))

	for k, v := range n {
		clone[k] = deepcopy.DeepCopy(v)
	}

	return clone
}
Example

package main

import (
	"fmt"

	"github.com/gohobby/deepcopy"
)

type Map map[string]interface{}

func (n Map) DeepCopy() interface{} {
	clone := make(Map, len(n))

	for k, v := range n {
		clone[k] = deepcopy.DeepCopy(v)
	}

	return clone
}

var nestedMap = Map{
	"flag": "🇫🇷",
	"country": Map{
		"city": "Paris",
	},
}

func main() {
	// Deep Copy
	deepClone := nestedMap.DeepCopy().(Map)

	// Change of the cloned object
	deepClone["flag"] = "🇮🇹"
	deepClone["country"].(Map)["city"] = "Roma"

	fmt.Printf("%#v\n", deepClone)
	// main.Map{"country":main.Map{"city":"Roma"}, "flag":"🇮🇹"} <-- ✅

	fmt.Printf("%#v\n\n", nestedMap)
	// main.Map{"country":main.Map{"city":"Paris"}, "flag":"🇫🇷"} <-- ✅

	fmt.Printf("%p\n", deepClone["country"]) // 0xc000012240
	fmt.Printf("%p\n", nestedMap["country"]) // 0xc0000121e0
}

Run this code in GoPlayground

Why?

Mutability

Map types are pointers which make them mutable objects.

When you write the statement

m := make(map[int]int)

The compiler replaces it with a call to runtime.makemap, which has the signature

// makemap implements Go map creation for make(map[k]v, hint).
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If h.buckets != nil, bucket pointed to can be used as the first bucket.
func makemap(t *maptype, hint int, h *hmap) *hmap

As you can see, the type of the value returned by runtime.makemap is a pointer to a runtime.hmap structure.

See Dave Cheney's article for more details.

Example

package main

import "fmt"

func main() {
	obj := map[string]int{"one": 1, "two": 2}

	obj2 := obj

	fmt.Printf("(obj)  %v\n(obj2) %v\n\n",
		obj,  // map[one:1 two:2]
		obj2, // map[one:1 two:2]
	)

	obj2["three"] = 3

	fmt.Printf("(obj2) %v\n", obj2)
	// map[one:1 three:3 two:2] <-- ✅
	fmt.Printf("(obj)  %v\n", obj)
	// map[one:1 three:3 two:2] <-- ❌
}

Run this code in GoPlayground

How to create copies of your maps?
Shallow Copy

A shallow copy means that the first level is copied, deeper levels are referenced.

shallowcopy
Example

package main

import "fmt"

var nestedObject = map[string]interface{}{
	"flag": "🇫🇷",
	"country": map[string]interface{}{
		"city": "Paris",
	},
}

func main() {
	// Shallow Copy
	shallowClone := make(map[string]interface{}, len(nestedObject))

	for k, v := range nestedObject {
		shallowClone[k] = v
	}

	// Change of the cloned object
	shallowClone["flag"] = "🇮🇹"
	shallowClone["country"].(map[string]interface{})["city"] = "Roma"

	fmt.Printf("%v\n", shallowClone)
	// map[country:map[city:Roma] flag:🇮🇹] <-- ✅

	fmt.Printf("%v\n\n", nestedObject)
	// map[country:map[city:Roma] flag:🇫🇷] <-- ❌ was mutated

	fmt.Printf("%p\n", shallowClone["country"]) // 0xc0000121e0
	fmt.Printf("%p\n", nestedObject["country"]) // 0xc0000121e0
}

Run this code in GoPlayground

Deep Copy

A deep copy is a shallow copy applied recursively to all sub objects.

deepcopy
Example

package main

import (
	"fmt"

	"github.com/gohobby/deepcopy"
)

var nestedObject = map[string]interface{}{
	"flag": "🇫🇷",
	"country": map[string]interface{}{
		"city": "Paris",
	},
}

func main() {
	// Deep Copy
	deepClone := deepcopy.Map(nestedObject).Clone()

	// Change of the cloned object
	deepClone["flag"] = "🇮🇹"
	deepClone["country"].(map[string]interface{})["city"] = "Roma"

	fmt.Printf("%v\n", deepClone)
	// map[country:map[city:Roma] flag:🇮🇹] <-- ✅

	fmt.Printf("%v\n\n", nestedObject)
	// map[country:map[city:Paris] flag:🇫🇷] <-- ✅

	fmt.Printf("%p\n", deepClone["country"])    // 0xc000012240
	fmt.Printf("%p\n", nestedObject["country"]) // 0xc0000121e0
}

Run this code in GoPlayground

Documentation

Overview

Package deepcopy provides a deep copy function for maps and slices.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeepCopy

func DeepCopy(object interface{}) interface{}

DeepCopy will create a deep copy of the source object. Maps and slices will be taken into account when copying.

Types

type Copyable

type Copyable interface {
	DeepCopy() interface{}
}

type Map

type Map map[string]interface{}

Map is a shortcut for map[string]interface{} which implements the Copyable interface.

func (Map) Clone

func (m Map) Clone() map[string]interface{}

Clone is a wrapper for Map.DeepCopy() that returns the correct type for convenience.

func (Map) DeepCopy

func (m Map) DeepCopy() interface{}

DeepCopy will create a deep copy of this map. Maps and slices will be taken into account when copying.

type Slice

type Slice []interface{}

Slice is a shortcut for []interface{} which implements the Copyable interface.

func (Slice) Clone

func (s Slice) Clone() []interface{}

Clone is a wrapper for Slice.DeepCopy() that returns the correct type for convenience.

func (Slice) DeepCopy

func (s Slice) DeepCopy() interface{}

DeepCopy will create a deep copy of this slice. Maps and slices will be taken into account when copying.

Directories

Path Synopsis
cmd
includes

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL