deepclone

package module
v0.2.16 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 3 Imported by: 3

README

DeepClone

A high-performance deep cloning library for Go that safely copies arbitrary values with one generic API

Go Module License Go Report Card

Features

  • Generic API: Clone any Go value with deepclone.Clone(value)
  • Fast common paths: Copy primitives, common slices, and common maps without reflection overhead
  • Circular reference safety: Preserve object graphs when cloning through the reflection path
  • Custom cloning: Implement Cloneable for domain-specific copy behavior
  • Concurrent use: Share the package safely across goroutines
  • No runtime dependencies: Core library uses only the Go standard library

Installation

go get github.com/kaptinlin/deepclone

Requires Go 1.26.3 or later.

Quick Start

package main

import (
    "fmt"

    "github.com/kaptinlin/deepclone"
)

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

    cloned := deepclone.Clone(original)
    original["numbers"][0] = 999

    fmt.Println(original["numbers"])
    fmt.Println(cloned["numbers"])
}

Output:

[999 2 3]
[1 2 3]

API Overview

API Purpose
Clone[T any](src T) T Return a deep copy of src
Cloneable Let a type provide its own clone implementation
CacheStats() (entries, fields int) Inspect cached struct metadata
ResetCache() Clear cached struct metadata, usually in tests and benchmarks

Usage

Clone structs and collections
type User struct {
    Name    string
    Friends []string
    Config  map[string]any
}

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

cloned := deepclone.Clone(user)
cloned.Friends[0] = "Eve"

user.Friends remains unchanged because slices, maps, arrays, structs, pointers, and interfaces are cloned recursively.

Customize clone behavior
type Document struct {
    Title   string
    Content []byte
    Version int
}

func (d Document) Clone() any {
    return Document{
        Title:   d.Title,
        Content: deepclone.Clone(d.Content),
        Version: d.Version + 1,
    }
}

Types that implement Cloneable control their own cloning behavior. Circular reference detection does not apply inside custom Clone methods.

Special cases
Value kind Clone behavior
Nil pointers, slices, and maps Preserved as nil
Functions Returned as-is
Channels Returned as the zero value of the same channel type
Unexported struct fields Left at the zero value

Performance

DeepClone keeps common operations fast with primitive, slice, and map fast paths plus cached reflection metadata for structs.

task bench

Run comparison benchmarks against other clone libraries:

task bench-comparison

Development

task deps   # Download and tidy dependencies
task lint   # Run golangci-lint and tidy checks
task test   # Run tests with -race
task verify # Run the full local verification pipeline

Examples

See examples/ for runnable examples covering basic values, circular references, and custom clone behavior.

License

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

Documentation

Overview

Package deepclone provides deep cloning with fast paths for common value, slice, and map types and reflection-based cloning for the rest.

Clone preserves object graphs, including circular references, when it falls back to reflection. Types that implement Cloneable provide their own cloning behavior and are responsible for handling cycles inside that method.

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 cached struct types and fields.

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 returns a deep copy of src.

Clone preserves circular references when it uses reflection. Types that implement Cloneable control their own cloning behavior.

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 cached struct metadata.

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 returns a copy that can be used independently of the original.
	Clone() any
}

Cloneable lets a type define its own deep-cloning behavior.

Clone must return a copy that can be used independently of the original. Circular reference detection does not apply inside custom Clone methods.

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