Documentation
¶
Overview ¶
Package deepclone provides high-performance deep cloning functionality for Go.
This package uses Go generics, reflection caching, and careful memory management to achieve zero-allocation hot paths where possible.
Basic Usage:
// Clone any value with full deep copy semantics
original := []int{1, 2, 3}
cloned := deepclone.Clone(original)
// Clone structs with nested data
type Config struct {
Tags []string
Meta map[string]int
}
cfg := Config{Tags: []string{"a"}, Meta: map[string]int{"x": 1}}
copy := deepclone.Clone(cfg)
Custom Cloning:
Types that implement the Cloneable interface receive custom cloning behavior instead of the default reflection-based deep clone:
func (m MyStruct) Clone() any {
return MyStruct{
Name: m.Name,
Data: deepclone.Clone(m.Data),
}
}
Performance Hierarchy:
Clone uses a hierarchical optimization strategy:
- Ultra-fast path: primitive types (zero allocation, direct return)
- Fast path: common slice and map types (generic copy, no reflection)
- Cloneable path: types implementing Cloneable interface
- Reflection path: all other types (cached struct info, circular ref detection)
Supported Types:
- All primitive types (int, string, bool, float, complex, etc.)
- Slices, maps, and arrays (with deep cloning of elements)
- Pointers and pointer chains (with circular reference detection)
- Structs (with automatic field-by-field deep cloning)
- Interfaces (with concrete type preservation)
- Custom types implementing Cloneable interface
- Channels return zero value; functions return as-is
Thread Safety:
All cloning operations are safe for concurrent use. The internal struct type cache uses sync.RWMutex with double-check locking for thread-safe access without contention on the hot path.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CacheStats ¶ added in v0.2.4
func CacheStats() (entries, fields int)
CacheStats returns the number of struct types currently cached and the total number of cached fields across all types.
This is useful for monitoring cache growth and validating that memory usage remains bounded. In practice, the entry count equals the number of distinct struct types cloned by the program, which is finite and compile-time determined.
CacheStats is safe for concurrent use.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
func main() {
deepclone.ResetCache()
type Point struct{ X, Y int }
_ = deepclone.Clone(Point{1, 2})
entries, fields := deepclone.CacheStats()
fmt.Println("entries:", entries)
fmt.Println("fields:", fields)
}
Output: entries: 1 fields: 2
func Clone ¶
func Clone[T any](src T) T
Clone creates a deep copy of the given value, preserving the complete object graph including circular references.
Clone uses a hierarchical optimization strategy for maximum performance:
- Primitive types (int, string, bool, etc.) return as-is with zero allocation
- Common slice types ([]int, []string, []byte, etc.) use optimized generic copy
- Common map types (map[string]string, etc.) use maps.Clone
- Types implementing Cloneable delegate to their Clone method
- All other types use cached reflection with circular reference detection
Special cases:
- nil values return as-is
- Channels return the zero value of their type
- Functions return as-is (cannot be deep cloned)
- Slice capacity and map size hints are preserved
Usage:
dst := deepclone.Clone(src)
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
func main() {
original := map[string][]int{
"scores": {90, 85, 77},
}
cloned := deepclone.Clone(original)
// Modify the clone — original is unaffected
cloned["scores"][0] = 100
fmt.Println("original:", original["scores"])
fmt.Println("cloned: ", cloned["scores"])
}
Output: original: [90 85 77] cloned: [100 85 77]
Example (Nil) ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
func main() {
var original []int
cloned := deepclone.Clone(original)
fmt.Println("nil preserved:", cloned == nil)
}
Output: nil preserved: true
Example (Slice) ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
func main() {
original := []string{"a", "b", "c"}
cloned := deepclone.Clone(original)
cloned[0] = "z"
fmt.Println("original:", original)
fmt.Println("cloned: ", cloned)
}
Output: original: [a b c] cloned: [z b c]
Example (Struct) ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
func main() {
type Address struct {
City string
State string
}
type Person struct {
Name string
Age int
Address *Address
}
original := Person{
Name: "Alice",
Age: 30,
Address: &Address{
City: "Portland",
State: "OR",
},
}
cloned := deepclone.Clone(original)
// Modify the clone's nested pointer — original is unaffected
cloned.Address.City = "Seattle"
cloned.Address.State = "WA"
fmt.Println("original:", original.Address.City, original.Address.State)
fmt.Println("cloned: ", cloned.Address.City, cloned.Address.State)
}
Output: original: Portland OR cloned: Seattle WA
func ResetCache ¶ added in v0.2.4
func ResetCache()
ResetCache clears the struct type cache, releasing all cached reflection data. Subsequent Clone operations will re-populate the cache on demand.
This is primarily useful in tests or long-running applications that dynamically load and unload plugins with unique struct types. Under normal usage the cache is small (bounded by the number of distinct struct types) and does not need to be reset.
ResetCache is safe for concurrent use.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
func main() {
type Coord struct{ X, Y int }
_ = deepclone.Clone(Coord{1, 2})
deepclone.ResetCache()
entries, _ := deepclone.CacheStats()
fmt.Println("entries after reset:", entries)
}
Output: entries after reset: 0
Types ¶
type Cloneable ¶
type Cloneable interface {
Clone() any
}
Cloneable allows types to implement custom deep cloning behavior. Types implementing this interface will have their Clone method called instead of using the default reflection-based cloning.
The Clone method should return a deep copy of the receiver. The implementer must ensure all nested data is properly deep copied to maintain complete independence from the original. Note that the library's built-in circular reference detection does not apply inside a custom Clone method; handle cycles manually if needed.
Example:
type Document struct {
Title string
Content []byte
Metadata map[string]any
}
func (d Document) Clone() any {
return Document{
Title: d.Title,
Content: deepclone.Clone(d.Content),
Metadata: deepclone.Clone(d.Metadata),
}
}
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/deepclone"
)
// Document is a type that implements the Cloneable interface
// to provide custom deep cloning behavior.
type Document struct {
Title string
Tags []string
}
func (d Document) Clone() any {
return Document{
Title: d.Title,
Tags: deepclone.Clone(d.Tags),
}
}
func main() {
original := Document{
Title: "Guide",
Tags: []string{"go", "clone"},
}
cloned := deepclone.Clone(original)
cloned.Tags[0] = "rust"
fmt.Println("original:", original.Tags)
fmt.Println("cloned: ", cloned.Tags)
}
Output: original: [go clone] cloned: [rust clone]
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Package main demonstrates basic usage of the deepclone library.
|
Package main demonstrates basic usage of the deepclone library. |
|
circular
command
Package main demonstrates handling circular references with the deepclone library.
|
Package main demonstrates handling circular references with the deepclone library. |
|
custom
command
Package main demonstrates custom cloning behavior with the deepclone library.
|
Package main demonstrates custom cloning behavior with the deepclone library. |