deepcopy

Deep-copies arbitrary Go values via reflection, recursing through structs, slices, maps, and pointers.

Library
Go
vv0.0.0-20170929034955-c48cc78d4826
631stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
34/100Needs Attention
Development Activity0
Maintenance0
Community56
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
51/100Fair
Architecture60
Code Quality55
Innovation35
Learning Curve55

deepcopy is a small, single-purpose Go library that produces a true deep copy of any value passed to it. Unlike a plain assignment or shallow copy, which only duplicates pointers, deepcopy walks the value with reflect and rebuilds structs, slices, maps, and pointer chains field by field so the copy shares no underlying memory with the original.

The package exposes one core entry point, Copy(src interface{}) interface{}, plus a deepcopy.Interface hook so a type can implement its own DeepCopy() interface{} method when the default reflection-based traversal isn’t appropriate. Unexported struct fields are intentionally skipped rather than copied, since they can’t be set via reflection from outside the package anyway.

What You Get

  • A single Copy() function that deep-copies any interface{} value using reflection
  • Recursive handling of pointers, interfaces, structs, slices, and maps
  • An Interface/DeepCopy() override hook for types that need custom copy logic
  • Special-cased handling for time.Time so time values copy correctly instead of being walked field by field
  • An Iface() alias kept for backwards compatibility with earlier API versions

Common Use Cases

  • Snapshotting configuration or state structs before mutating them, so the original can be restored or compared later
  • Cloning request/response structs in test fixtures so tests don’t share mutable state
  • Copying cached data structures before handing them to a caller that might mutate them
  • Duplicating nested map/slice-based data (e.g. parsed JSON as map[string]interface{}) without aliasing

Under The Hood

Architecture The entire package lives in one file, deepcopy.go, exposing a single public entry point, Copy(src interface{}) interface{}, that delegates to an unexported recursive helper, copyRecursive(original, cpy reflect.Value). There is no layering to speak of: the exported API is a thin wrapper, and all the real logic is a single type-switch over reflect.Kind (Ptr, Interface, Struct, Slice, Map, and a default case) that recurses into itself for nested values. The one architectural extension point is the Interface/DeepCopy() hook, checked first in copyRecursive via a type assertion, letting a caller’s type override the reflection-based traversal entirely. Because everything funnels through this one function, changing how any single kind is copied is a localized, low-risk edit, but there is no separation between traversal, allocation, and copy-semantics concerns.

Tech Stack deepcopy has zero external dependencies — it imports only the standard library’s reflect and time packages. There is no go.mod in the repository, meaning it predates (or has never adopted) Go modules and is consumed as a GOPATH-style import path (github.com/mohae/deepcopy), which still works fine under Go modules via go get with an inferred version. Continuous integration is configured via a legacy .travis.yml targeting older Go toolchains; there is no modern CI (GitHub Actions) configuration in the repo.

Code Quality The library ships with an unusually large test file relative to the implementation (deepcopy_test.go is roughly 9x the size of deepcopy.go), covering strings, bools, bytes, ints, uints, structs, slices, maps, pointers, interfaces, and time.Time values. Tests verify deep-copy correctness by comparing reflect.SliceHeader/pointer data addresses via unsafe, confirming that copies don’t alias the original’s backing memory rather than just checking equal values. That said, the tests are written as one long function per type category using goto labels to fall through between sections rather than table-driven subtests (t.Run), which was a common style before Go’s subtest support matured. Error handling is minimal by design — the package has no error return path at all, and unsupported/edge-case kinds silently fall through to the default cpy.Set(original) case. No linter or formatter configuration is present in the repo.

What Makes It Unique The reflection-based deep-copy pattern itself is a well-established one in the Go ecosystem, and this package doesn’t add anything beyond it — no cycle detection for circular references, no struct-tag-driven field exclusion, and no benchmarked performance comparisons against alternatives. Its value is in being an early, focused, dependency-free implementation of the pattern with unusually thorough correctness tests for the specific case of pointer/slice/map memory independence, along with the DeepCopy() override hook so higher-level types can special-case their own copy behavior when reflection alone isn’t sufficient.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search