{"id":17180212,"url":"https://github.com/huandu/go-clone","last_synced_at":"2025-05-16T10:05:42.575Z","repository":{"id":41369816,"uuid":"200779047","full_name":"huandu/go-clone","owner":"huandu","description":"Clone any Go data structure deeply and thoroughly.","archived":false,"fork":false,"pushed_at":"2025-04-29T15:01:29.000Z","size":107,"stargazers_count":314,"open_issues_count":2,"forks_count":30,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-16T10:04:27.006Z","etag":null,"topics":["algorithm","clone","deepcopy","go","immutable","reflect","utility"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/huandu/go-clone","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/huandu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-08-06T04:55:49.000Z","updated_at":"2025-05-14T20:03:21.000Z","dependencies_parsed_at":"2024-06-18T13:38:17.028Z","dependency_job_id":"de5ca89d-be6d-4262-9e30-94282ec76d0f","html_url":"https://github.com/huandu/go-clone","commit_stats":{"total_commits":51,"total_committers":1,"mean_commits":51.0,"dds":0.0,"last_synced_commit":"8f6897c4a11b99b77d25883c022f29159fda6e4c"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huandu%2Fgo-clone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huandu%2Fgo-clone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huandu%2Fgo-clone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huandu%2Fgo-clone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/huandu","download_url":"https://codeload.github.com/huandu/go-clone/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254509476,"owners_count":22082891,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","clone","deepcopy","go","immutable","reflect","utility"],"created_at":"2024-10-15T00:29:07.640Z","updated_at":"2025-05-16T10:05:42.554Z","avatar_url":"https://github.com/huandu.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# go-clone: Clone any Go data structure deeply and thoroughly\n\n[![Go](https://github.com/huandu/go-clone/workflows/Go/badge.svg)](https://github.com/huandu/go-clone/actions)\n[![Go Doc](https://godoc.org/github.com/huandu/go-clone?status.svg)](https://pkg.go.dev/github.com/huandu/go-clone)\n[![Go Report](https://goreportcard.com/badge/github.com/huandu/go-clone)](https://goreportcard.com/report/github.com/huandu/go-clone)\n[![Coverage Status](https://coveralls.io/repos/github/huandu/go-clone/badge.svg?branch=master)](https://coveralls.io/github/huandu/go-clone?branch=master)\n\nPackage `clone` provides functions to deep clone any Go data. It also provides a wrapper to protect a pointer from any unexpected mutation.\n\nFor users who use Go 1.18+, it's recommended to import `github.com/huandu/go-clone/generic` for generic APIs and arena support.\n\n`Clone`/`Slowly` can clone unexported fields and \"no-copy\" structs as well. Use this feature wisely.\n\n## Install\n\nUse `go get` to install this package.\n\n```shell\ngo get github.com/huandu/go-clone\n```\n\n## Usage\n\n### `Clone` and `Slowly`\n\nIf we want to clone any Go value, use `Clone`.\n\n```go\nt := \u0026T{...}\nv := clone.Clone(t).(*T)\nreflect.DeepEqual(t, v) // true\n```\n\nFor the sake of performance, `Clone` doesn't deal with values containing pointer cycles.\nIf we need to clone such values, use `Slowly` instead.\n\n```go\ntype ListNode struct {\n    Data int\n    Next *ListNode\n}\nnode1 := \u0026ListNode{\n    Data: 1,\n}\nnode2 := \u0026ListNode{\n    Data: 2,\n}\nnode3 := \u0026ListNode{\n    Data: 3,\n}\nnode1.Next = node2\nnode2.Next = node3\nnode3.Next = node1\n\n// We must use `Slowly` to clone a circular linked list.\nnode := Slowly(node1).(*ListNode)\n\nfor i := 0; i \u003c 10; i++ {\n    fmt.Println(node.Data)\n    node = node.Next\n}\n```\n\n### Generic APIs\n\nStarting from go1.18, Go started to support generic. With generic syntax, `Clone`/`Slowly` and other APIs can be called much cleaner like following.\n\n```go\nimport \"github.com/huandu/go-clone/generic\"\n\ntype MyType struct {\n    Foo string\n}\n\noriginal := \u0026MyType{\n    Foo: \"bar\",\n}\n\n// The type of cloned is *MyType instead of interface{}.\ncloned := Clone(original)\nprintln(cloned.Foo) // Output: bar\n```\n\nIt's required to update minimal Go version to 1.18 to opt-in generic syntax. It may not be a wise choice to update this package's `go.mod` and drop so many old Go compilers for such syntax candy. Therefore, I decide to create a new standalone package `github.com/huandu/go-clone/generic` to provide APIs with generic syntax.\n\nFor new users who use Go 1.18+, the generic package is preferred and recommended.\n\n### Arena support\n\nStarting from Go1.20, arena is introduced as a new way to allocate memory. It's quite useful to improve overall performance in special scenarios.\nIn order to clone a value with memory allocated from an arena, there are new methods `ArenaClone` and `ArenaCloneSlowly` available in `github.com/huandu/go-clone/generic`.\n\n```go\n// ArenaClone recursively deep clones v to a new value in arena a.\n// It works in the same way as Clone, except it allocates all memory from arena.\nfunc ArenaClone[T any](a *arena.Arena, v T) (nv T) \n\n// ArenaCloneSlowly recursively deep clones v to a new value in arena a.\n// It works in the same way as Slowly, except it allocates all memory from arena.\nfunc ArenaCloneSlowly[T any](a *arena.Arena, v T) (nv T)\n```\n\nDue to limitations in arena API, memory of the internal data structure of `map` and `chan` is always allocated in heap by Go runtime ([see this issue](https://github.com/golang/go/issues/56230)).\n\n**Warning**: Per [discussion in the arena proposal](https://github.com/golang/go/issues/51317), the arena package may be changed incompatibly or removed in future. All arena related APIs in this package will be changed accordingly.\n\n### Struct tags\n\nThere are some struct tags to control how to clone a struct field.\n\n```go\ntype T struct {\n    Normal *int\n    Foo    *int `clone:\"skip\"`       // Skip cloning this field so that Foo will be zero in cloned value.\n    Bar    *int `clone:\"-\"`          // \"-\" is an alias of skip.\n    Baz    *int `clone:\"shadowcopy\"` // Copy this field by shadow copy.\n}\n\na := 1\nt := \u0026T{\n    Normal: \u0026a,\n    Foo:    \u0026a,\n    Bar:    \u0026a,\n    Baz:    \u0026a,\n}\nv := clone.Clone(t).(*T)\n\nfmt.Println(v.Normal == t.Normal) // false\nfmt.Println(v.Foo == nil)         // true\nfmt.Println(v.Bar == nil)         // true\nfmt.Println(v.Baz == t.Baz)       // true\n```\n\n### Memory allocations and the `Allocator`\n\nThe `Allocator` is designed to allocate memory when cloning. It's also used to hold all customizations, e.g. custom clone functions, scalar types and opaque pointers, etc. There is a default allocator which allocates memory from heap. Almost all public APIs in this package use this default allocator to do their job.\n\nWe can control how to allocate memory by creating a new `Allocator` by `NewAllocator`. It enables us to take full control over memory allocation when cloning. See [Allocator sample code](https://pkg.go.dev/github.com/huandu/go-clone#example-Allocator) to understand how to customize an allocator.\n\nLet's take a closer look at the `NewAllocator` function.\n\n```go\nfunc NewAllocator(pool unsafe.Pointer, methods *AllocatorMethods) *Allocator\n```\n\n- The first parameter `pool` is a pointer to a memory pool. It's used to allocate memory for cloning. It can be `nil` if we don't need a memory pool.\n- The second parameter `methods` is a pointer to a struct which contains all methods to allocate memory. It can be `nil` if we don't need to customize memory allocation.\n- The `Allocator` struct is allocated from the `methods.New` or the `methods.Parent` allocator or from heap.\n\nThe `Parent` in `AllocatorMethods` is used to indicate the parent of the new allocator. With this feature, we can orgnize allocators into a tree structure. All customizations, including custom clone functions, scalar types and opaque pointers, etc, are inherited from parent allocators.\n\nThere are some APIs designed for convenience.\n\n- We can create dedicated allocators for heap or arena by calling `FromHeap()` or `FromArena(a *arena.Arena)`.\n- We can call `MakeCloner(allocator)` to create a helper struct with `Clone` and `CloneSlowly` methods in which the type of in and out parameters is `interface{}`.\n\n### Mark struct type as scalar\n\nSome struct types can be considered as scalar.\n\nA well-known case is `time.Time`.\nAlthough there is a pointer `loc *time.Location` inside `time.Time`, we always use `time.Time` by value in all methods.\nWhen cloning `time.Time`, it should be OK to return a shadow copy.\n\nCurrently, following types are marked as scalar by default.\n\n- `time.Time`\n- `reflect.Value`\n\nIf there is any type defined in built-in package should be considered as scalar, please open new issue to let me know.\nI will update the default.\n\nIf there is any custom type should be considered as scalar, call `MarkAsScalar` to mark it manually. See [MarkAsScalar sample code](https://pkg.go.dev/github.com/huandu/go-clone#example-MarkAsScalar) for more details.\n\n### Mark pointer type as opaque\n\nSome pointer values are used as enumerable const values.\n\nA well-known case is `elliptic.Curve`. In package `crypto/tls`, curve type of a certificate is checked by comparing values to pre-defined curve values, e.g. `elliptic.P521()`. In this case, the curve values, which are pointers or structs, cannot be cloned deeply.\n\nCurrently, following types are marked as scalar by default.\n\n- `elliptic.Curve`, which is `*elliptic.CurveParam` or `elliptic.p256Curve`.\n- `reflect.Type`, which is `*reflect.rtype` defined in `runtime`.\n\nIf there is any pointer type defined in built-in package should be considered as opaque, please open new issue to let me know.\nI will update the default.\n\nIf there is any custom pointer type should be considered as opaque, call `MarkAsOpaquePointer` to mark it manually. See [MarkAsOpaquePointer sample code](https://pkg.go.dev/github.com/huandu/go-clone#example-MarkAsOpaquePointer) for more details.\n\n### Clone \"no-copy\" types defined in `sync` and `sync/atomic`\n\nThere are some \"no-copy\" types like `sync.Mutex`, `atomic.Value`, etc.\nThey cannot be cloned by copying all fields one by one, but we can alloc a new zero value and call methods to do proper initialization.\n\nCurrently, all \"no-copy\" types defined in `sync` and `sync/atomic` can be cloned properly using following strategies.\n\n- `sync.Mutex`: Cloned value is a newly allocated zero mutex.\n- `sync.RWMutex`: Cloned value is a newly allocated zero mutex.\n- `sync.WaitGroup`: Cloned value is a newly allocated zero wait group.\n- `sync.Cond`: Cloned value is a cond with a newly allocated zero lock.\n- `sync.Pool`: Cloned value is an empty pool with the same `New` function.\n- `sync.Map`: Cloned value is a sync map with cloned key/value pairs.\n- `sync.Once`: Cloned value is a once type with the same done flag.\n- `atomic.Value`/`atomic.Bool`/`atomic.Int32`/`atomic.Int64`/`atomic.Uint32`/`atomic.Uint64`/`atomic.Uintptr`: Cloned value is a new atomic value with the same value.\n\nIf there is any type defined in built-in package should be considered as \"no-copy\" types, please open new issue to let me know.\nI will update the default.\n\n### Set custom clone functions\n\nIf default clone strategy doesn't work for a struct type, we can call `SetCustomFunc` to register a custom clone function.\n\n```go\nSetCustomFunc(reflect.TypeOf(MyType{}), func(allocator *Allocator, old, new reflect.Value) {\n    // Customized logic to copy the old to the new.\n    // The old's type is MyType.\n    // The new is a zero value of MyType and new.CanAddr() always returns true.\n})\n```\n\nWe can use `allocator` to clone any value or allocate new memory.\nIt's allowed to call `allocator.Clone` or `allocator.CloneSlowly` on `old` to clone its struct fields in depth without worrying about dead loop.\n\nSee [SetCustomFunc sample code](https://pkg.go.dev/github.com/huandu/go-clone#example-SetCustomFunc) for more details.\n\n### Clone `atomic.Pointer[T]`\n\nAs there is no way to predefine a custom clone function for generic type `atomic.Pointer[T]`, cloning such atomic type is not supported by default. If we want to support it, we need to register a custom clone function manually.\n\nSuppose we instantiate `atomic.Pointer[T]` with type `MyType1` and `MyType2` in a project, and then we can register custom clone functions like following.\n\n```go\nimport \"github.com/huandu/go-clone/generic\"\n\nfunc init() {\n    // Register all instantiated atomic.Pointer[T] types in this project.\n    clone.RegisterAtomicPointer[MyType1]()\n    clone.RegisterAtomicPointer[MyType2]()\n}\n```\n\n### `Wrap`, `Unwrap` and `Undo`\n\nPackage `clone` provides `Wrap`/`Unwrap` functions to protect a pointer value from any unexpected mutation.\nIt's useful when we want to protect a variable which should be immutable by design,\ne.g. global config, the value stored in context, the value sent to a chan, etc.\n\n```go\n// Suppose we have a type T defined as following.\n//     type T struct {\n//         Foo int\n//     }\nv := \u0026T{\n    Foo: 123,\n}\nw := Wrap(v).(*T) // Wrap value to protect it.\n\n// Use w freely. The type of w is the same as that of v.\n\n// It's OK to modify w. The change will not affect v.\nw.Foo = 456\nfmt.Println(w.Foo) // 456\nfmt.Println(v.Foo) // 123\n\n// Once we need the original value stored in w, call `Unwrap`.\norig := Unwrap(w).(*T)\nfmt.Println(orig == v) // true\nfmt.Println(orig.Foo)  // 123\n\n// Or, we can simply undo any change made in w.\n// Note that `Undo` is significantly slower than `Unwrap`, thus\n// the latter is always preferred.\nUndo(w)\nfmt.Println(w.Foo) // 123\n```\n\n## Performance\n\nHere is the performance data running on my dev machine.\n\n```text\ngo 1.20.1\ngoos: darwin\ngoarch: amd64\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkSimpleClone-12       7164530        156.7 ns/op       24 B/op        1 allocs/op\nBenchmarkComplexClone-12       628056         1871 ns/op     1488 B/op       21 allocs/op\nBenchmarkUnwrap-12           15498139        78.02 ns/op        0 B/op        0 allocs/op\nBenchmarkSimpleWrap-12        3882360        309.7 ns/op       72 B/op        2 allocs/op\nBenchmarkComplexWrap-12        949654         1245 ns/op      736 B/op       15 allocs/op\n```\n\n## License\n\nThis package is licensed under MIT license. See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuandu%2Fgo-clone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhuandu%2Fgo-clone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuandu%2Fgo-clone/lists"}