deepclone

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2026 License: MIT Imports: 3 Imported by: 2

README

DeepClone

A high-performance deep cloning library for Go that provides safe, efficient copying of any Go value.

Go Version License Go Report Card

Features

  • High Performance: Zero-allocation fast paths for primitive types
  • Circular Reference Safe: Automatic detection and handling of circular references
  • Thread Safe: Concurrent operations with safe caching mechanisms
  • Universal Support: Works with all Go types including channels, functions, and interfaces
  • Extensible: Custom cloning behavior via Cloneable interface
  • Zero Dependencies: Uses only Go standard library

Installation

go get github.com/kaptinlin/deepclone

Quick Start

package main

import (
    "fmt"
    "github.com/kaptinlin/deepclone"
)

func main() {
    // Deep clone any value
    original := map[string][]int{
        "numbers": {1, 2, 3},
        "scores":  {85, 90, 95},
    }

    cloned := deepclone.Clone(original)

    // Modify original - cloned remains independent
    original["numbers"][0] = 999

    fmt.Println("Original:", original["numbers"]) // [999, 2, 3]
    fmt.Println("Cloned:", cloned["numbers"])     // [1, 2, 3]
}

Core Concept

All operations perform deep copies by default:

  • Primitives: int, string, bool -- Copied by value (zero allocations)
  • Collections: slice, map, array -- New containers with cloned elements
  • Structs: New instances with all fields deeply cloned
  • Pointers: New pointers pointing to cloned values
  • Custom Types: Support via Cloneable interface

Examples

Basic Usage
// Primitives (zero allocation)
number := deepclone.Clone(42)
text := deepclone.Clone("hello")

// Collections (deep cloned)
slice := deepclone.Clone([]string{"a", "b", "c"})
data := deepclone.Clone(map[string]int{"key": 42})

// Complex structures
type User struct {
    Name    string
    Friends []string
    Config  map[string]interface{}
}

user := User{
    Name:    "Alice",
    Friends: []string{"Bob", "Charlie"},
    Config:  map[string]interface{}{"theme": "dark"},
}

cloned := deepclone.Clone(user) // Complete deep copy
Custom Cloning Behavior
type Document struct {
    Title   string
    Content []byte
    Version int
}

// Implement custom cloning logic
func (d Document) Clone() any {
    return Document{
        Title:   d.Title,
        Content: deepclone.Clone(d.Content).([]byte),
        Version: d.Version + 1, // Increment version on clone
    }
}

doc := Document{Title: "My Doc", Version: 1}
cloned := deepclone.Clone(doc) // Version becomes 2

For more examples, see examples/ directory.

Performance

DeepClone is optimized for performance with:

  • Zero allocations for primitive types
  • Fast paths for common slice/map types
  • Reflection caching for struct types
  • Minimal overhead for complex operations
Benchmark Results

Tested on Apple M3, macOS (darwin/arm64):

Operation Performance Memory Allocations
Primitives (int/string/bool) 2.7-3.6 ns/op 0 B/op 0 allocs/op
Slice (100 ints) 200.6 ns/op 896 B/op 1 allocs/op
Map (100 entries) 4,299 ns/op 3,544 B/op 4 allocs/op
Simple Struct 248.6 ns/op 128 B/op 4 allocs/op
Nested Struct 1,386 ns/op 952 B/op 19 allocs/op
Large Slice (10K ints) 6,709 ns/op 81,920 B/op 1 allocs/op

For detailed benchmarks and comparisons with other libraries, see benchmarks/.

# Run benchmarks
cd benchmarks && go test -bench=. -benchmem

API Reference

Core Function
func Clone[T any](src T) T

Creates a deep copy of any value. The returned value is completely independent of the original.

Custom Cloning Interface
type Cloneable interface {
    Clone() any
}

Implement this interface to provide custom cloning behavior for your types.

Advanced Features

  • Circular Reference Detection: Prevents infinite loops in self-referencing structures
  • Interface Preservation: Maintains original interface types while cloning concrete values
  • Thread Safety: All operations are safe for concurrent use
  • Type Caching: Struct metadata is cached for improved performance on repeated operations

Contributing

We welcome contributions! Please feel free to:

  • Report bugs
  • Suggest new features
  • Submit pull requests
  • Improve documentation

Requirements

  • Go 1.25 or later
  • No external dependencies

License

This project is licensed under the MIT License - see the LICENSE file for details.

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 (entries) and the total number of cached fields across all types (fields).

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.

Jump to

Keyboard shortcuts

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