// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package copy import ( "reflect" ) // DeepCopyValue produces a deep copy of the given value, where the result // ideally shares no mutable memory with the given value. // // There are some limitations on what's possible, however: // - This package can't write into an unexported field of a struct, so those // will be ignored entirely and thus left as their zero values in the // result. // - If the given structure contains function pointers then their closures // might refer to shared memory that this function cannot copy. If they // refer to memory that's also included in the data structure outside of // the function pointer then the two will be disconnected in the result. // - It isn't really meaningful to "copy" a channel since it's a // synchronization primitive rather than a data structure, so the result // will share the same channels as the input. // - Copying other library-based synchronization primitives like [sync.Mutex] // doesn't really make sense either, although this function doesn't // understand what they are and so the result is undefined. If your // synchronization primitive has a "ready to use" zero value then it _might_ // be acceptable to store it in an unexported field and thus have it be // zeroed in the result, but at that point you're probably better off // writing a specialized deepcopy function so that you can actually use // the synchronization primitive to prevent data races during copying. // - The uintptr and [unsafe.Pointer] types might well refer to some shared // memory, but don't give any information about how to copy that memory // and so those are just preserved verbatim, making the result point to // the same memory as the input. // - Broadly, this function needs special handling for each different kind // of value in Go, and so if a later version of Go has introduced a new kind // of value then this function might not support it yet. That might cause // this function to panic, or to ignore part of the structure, or otherwise // misbehave. // // This is intended as a relatively simple utility for straightforward cases, // primarily for use in contrived situations like unit tests. // // It intentionally does not offer any customization; if you need to do // something special then it's better to just write your own simple direct code // than to pull in all of this reflection trickery. Even if you don't need to do // something special it's probably still better to just write some // straightforward code that directly describes the behavior you're intending, // so that the Go compiler can help you and so you don't force future // maintainers to understand all of this metaprogramming if something goes // wrong. Seriously... don't use this function. func DeepCopyValue[T any](v T) T { // We use type parameters in the signature to make usage more convenient // for the caller (no type assertions required) but we actually do all // our internal work in the realm of package reflect. input := reflect.ValueOf(&v).Elem() // if T is an interface type then input is the interface value, not the value inside it ty := reflect.TypeFor[T]() // likewise, if T is interface type then this is the static interface type, not the dynamic type result := deepCopyValue(input, ty) return result.Interface().(T) } func deepCopyValue(v reflect.Value, ty reflect.Type) reflect.Value { switch ty.Kind() { // A large subset of the kinds don't point to mutable sharable memory, // or don't refer to something we can possibly copy, and so we can just // return them directly without any extra work. case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String, reflect.UnsafePointer, reflect.Func, reflect.Chan: return v case reflect.Array: return deepCopyArray(v, ty) case reflect.Interface: return deepCopyInterface(v, ty) case reflect.Map: return deepCopyMap(v, ty) case reflect.Pointer: return deepCopyPointer(v, ty) case reflect.Slice: return deepCopySlice(v, ty) case reflect.Struct: return deepCopyStruct(v, ty) default: panic("unsupported type kind " + ty.Kind().String()) } } func deepCopyArray(v reflect.Value, ty reflect.Type) reflect.Value { // Copying an array really means allocating a new array and then // copying each of the elements from the source. ret := reflect.New(ty).Elem() for i := range ret.Len() { newElemV := deepCopyValue(v.Index(i), ty.Elem()) ret.Index(i).Set(newElemV) } return ret } func deepCopyInterface(v reflect.Value, ty reflect.Type) reflect.Value { if v.IsNil() { return v } // An interface value is not directly mutable itself, but the value // inside it might be and so we'll copy that and then wrap the result // in a new interface value of the same type. ret := reflect.New(ty).Elem() dynV := deepCopyValue(v.Elem(), v.Elem().Type()) ret.Set(dynV) return ret } func deepCopyMap(v reflect.Value, ty reflect.Type) reflect.Value { if v.IsNil() { return v } ret := reflect.MakeMap(ty) for iter := v.MapRange(); iter.Next(); { // We don't copy the key because Go does not allow any mutably-aliasable // types as map keys. (That would make it very easy to corrupt the // internals of the map, after all!) k := iter.Key() v := deepCopyValue(iter.Value(), ty.Elem()) ret.SetMapIndex(k, v) } return ret } func deepCopyPointer(v reflect.Value, ty reflect.Type) reflect.Value { if v.IsNil() { return v } // We copy a pointer by copying what it refers to and then returning // a pointer to that copy. newTarget := deepCopyValue(v.Elem(), ty.Elem()) return newTarget.Addr() } func deepCopySlice(v reflect.Value, ty reflect.Type) reflect.Value { if v.IsNil() { return v } // Copying a slice really means copying the part of its backing array // that in could potentially observe. In particular, it's possible to // expand the view of the backing array up to the slice's capacity, // so we need to copy the entire capacity even if the length is // currently shorter to ensure that the result is truly equivalent. length := v.Len() capacity := v.Cap() // This exposes any elements that are between length and capacity. fullView := v.Slice3(0, capacity, capacity) // Making a slice also allocates a new backing array for it. ret := reflect.MakeSlice(ty, capacity, capacity) for i := range capacity { ret.Index(i).Set(fullView.Index(i)) } // We must restore the original length before we return. return ret.Slice(0, length) } func deepCopyStruct(v reflect.Value, ty reflect.Type) reflect.Value { // To copy a struct we must copy each exported field one by one. // We can't assign to unexported fields and so we just leave those // unset in the new value. ret := reflect.New(ty).Elem() for i := range ty.NumField() { fieldRet := ret.Field(i) if !fieldRet.CanSet() { // Presumably it's an unexported field, so we can't do anything // with it and must leave it zeroed. continue } newVal := deepCopyValue(v.Field(i), ty.Field(i).Type) fieldRet.Set(newVal) } return ret }