deepcopy icon indicating copy to clipboard operation
deepcopy copied to clipboard

There are cases we cannot deepcopy

Open HarutakaMatsumoto opened this issue 4 years ago • 7 comments
trafficstars

Hello!

Somehow Copy function cannot deepcopy an array of list of list. A example test is following.

package deepcopytest

import (
	"testing"

	"github.com/mohae/deepcopy"
)

type a [3][][]float64

var source = a{
	{
		{
			1.0,
			2.0,
			3.0,
		},
	},
}

func TestDeepcopy(t *testing.T) {
	distination := deepcopy.Copy(source).(a)
	source[0][0][0] = 4.0
	source[0][0][1] = 5.0
	source[0][0][2] = 6.0

	if distination[0][0][0] == source[0][0][0] {
		t.Fatal("Isn't deepcopied!", distination, source)
	}
}

Please make it can deepcopy. Thank you.

HarutakaMatsumoto avatar Oct 13 '21 07:10 HarutakaMatsumoto

Here is a live demo on the Go playground with a simplified test case: type [1][]int.

dolmen avatar Feb 02 '22 10:02 dolmen

` case reflect.Array: nv := reflect.New(original.Type()) arrCopyValue := nv.Elem() num := original.Len()

	for i := 0; i < num; i++ {
		itemValue := original.Index(i)
		copyValue := reflect.New(itemValue.Type()).Elem()
		copyRecursive(original.Index(i), copyValue)
		arrCopyValue.Index(i).Set(copyValue)
	}

	cpy.Set(arrCopyValue)

`

DenisYin66 avatar Dec 29 '22 09:12 DenisYin66

The author treats the array behavior wrongly, which relies on its deep copy entirely. Any reference type array will be affected by this case. Here are more test cases. @HarutakaMatsumoto I will try to create a fix recently.

func TestArrayOfSomething(t *testing.T) {
	verifyCases := map[string]struct {
		modifyAndVerify func(t *testing.T)
	}{
		// failed because the map(underlying is a pointer) will be copied directly
		"array of map": {
			modifyAndVerify: func(t *testing.T) {
				origin := [1]map[string]int{
					{
						"1": 1,
					},
				}
				copied := deepcopy.Copy(&origin)
				assert.NotSame(t, origin, copied)
				assert.NotSame(t, origin[0], copied.(*[1]map[string]int)[0])
				origin[0]["1"] = 999
				assert.Equal(t, 1, copied.(*[1]map[string]int)[0]["1"])
			},
		},
		// failed because the pointer of map will be copied directly
		"array of *map": {
			modifyAndVerify: func(t *testing.T) {
				origin := [1]*map[string]int{
					{
						"1": 1,
					},
				}
				copied := deepcopy.Copy(&origin)
				assert.NotSame(t, origin, copied)
				assert.NotSame(t, origin[0], copied.(*[1]*map[string]int)[0])
				(*origin[0])["1"] = 999
				assert.Equal(t, 1, (*copied.(*[1]*map[string]int)[0])["1"])
			},
		},
		// failed because the pointer of int will be copied directly
		"array of *int": {
			modifyAndVerify: func(t *testing.T) {
				intp := func(i int) *int {
					return &i
				}
				arrayOfInt := [3]*int{intp(1), intp(2)}
				copied := deepcopy.Copy(&arrayOfInt)
				assert.NotSame(t, &arrayOfInt, copied)
				assert.NotSame(t, arrayOfInt[0], copied.(*[3]*int)[0])
				arrayOfInt[0] = intp(999)
				assert.Equal(t, 1, *copied.(*[3]*int)[0])
			},
		},
		// succeed because int will be copied by value
		"array of int": {
			modifyAndVerify: func(t *testing.T) {
				arrayOfInt := [3]int{1, 2}
				copied := deepcopy.Copy(&arrayOfInt)
				assert.NotSame(t, &arrayOfInt, copied)
				assert.NotSame(t, &arrayOfInt[0], copied.(*[3]int)[0])
				arrayOfInt[0] = 999
				assert.Equal(t, 1, copied.(*[3]int)[0])
			},
		},
	}
	for key, tt := range verifyCases {
		t.Run(key, tt.modifyAndVerify)
	}
}

xieyuschen avatar Aug 01 '23 03:08 xieyuschen

Hello @xieyuschen . Oh! Thank you for your fixing! I try to use it.

HarutakaMatsumoto avatar Oct 16 '23 08:10 HarutakaMatsumoto