doppel
Your data's doppelgänger — deep copies without side effects.
doppel is a Go library for safe, explicit deep cloning of complex data structures. It provides a minimal, zero-reflection API built around composable generic helpers that integrate directly into your type's Clone() method.
Go assignment is a shallow copy. Structs with pointer fields, slices, and maps silently share memory between "originals" and "copies," leading to subtle bugs. doppel solves this by giving you full control over every field, every allocation, and every edge case.
| Principle | What it means |
|---|---|
| Explicit | Every clone path is visible and auditable — no hidden behavior. |
| Composable | CloneSlice, CloneMap, ClonePointer wire together in your Clone(). |
| Zero reflection | No magic, maximum speed. Generics-based helpers with no runtime overhead. |
| Code generation | Optional doppelgen tool generates Clone() methods from struct tags. |
core — Interfaces: Cloner[T], SelfClonable[T], FuncCloner[T], error types, tag contract
manual — Generic helpers: CloneSlice, CloneMap, ClonePointer, Identity
doppel — Top-level API: Clone[T], MustClone[T]
doppel.Clone(user) → user.Clone() → manual.CloneSlice + CloneMap + ClonePointer
(zero overhead) (fully visible, fully controlled)
There is no priority chain, no registry, and no reflection. You own the clone path.
type User struct {
ID int64
Name string
Active bool
Tags []string
Scores map[string]int
}func (u *User) Clone() (*User, error) {
if u == nil { return nil, nil }
tags, err := manual.CloneSlice(u.Tags, manual.Identity[string])
if err != nil {
return nil, core.WrapError("User.Tags", err)
}
scores, err := manual.CloneMap(u.Scores, manual.Identity[int])
if err != nil {
return nil, core.WrapError("User.Scores", err)
}
return &User{ID: u.ID, Name: u.Name, Active: u.Active, Tags: tags, Scores: scores}, nil
}cloned, err := doppel.Clone(original)
// Mutate the original — the clone is unaffected
original.Tags[0] = "mutated"
// cloned.Tags[0] still has the original valuedoppelgen reads your Go source files, finds structs annotated with doppel:"..." tags, and generates *.clone_gen.go files that implement SelfClonable[T].
It also generates companion *.clone_gen_test.go test files that verify nil safety, deep-copy independence, skip/shallow/empty field semantics, and more.
Add the following directive to any go file (typically in any main like file).
//go:generate go run github.com/seyallius/doppel/cmd/doppelgen --type=MyStructThen run:
go generate ./...The generator produces files like user.clone_gen.go with a header
Code generated by doppelgen. DO NOT EDIT.
A runnable example is present in main.go. It generates structs for types in testdata in preview mode (remove --preview for actual file generation).
If you want full control, install doppelgen locally (see doppelgen installation) and run the CLI yourself.
| Flag | Description |
|---|---|
--type |
Comma-separated struct names |
--package |
Target package directory (default: same) |
--output |
Output directory |
--tag |
Custom struct tag name (default: doppel) |
--preview |
Print generated code to stdout |
// Using in code
go get github.com/seyallius/doppel
// Installing doppelgen
go install github.com/seyallius/doppel/cmd/doppelgen@latestRequires Go 1.25 or later.
- Zero-reflection cloning — generics-based helpers, no runtime overhead
- Composable helpers —
CloneSlice,CloneMap,ClonePointerwire together inClone() - Contextual errors — field-path annotated errors with
errors.Is/errors.Assupport - Nil-safety — all helpers preserve nil/empty distinction
- Thread-safe — all public types are safe for concurrent use
- Struct tags —
doppel:"-",doppel:"shallow",doppel:"clone",doppel:"deep"for code generation - Zero dependencies — only the Go standard library
| Approach | ns/op | B/op | allocs/op |
|---|---|---|---|
Manual Clone() (User) |
~750 | ~480 | ~12 |
Manual Clone() (Order) |
~1200 | ~800 | ~20 |
| Shallow copy (baseline) | ~1 | 0 | 0 |
See Benchmarks for detailed data.
| # | Page | Topic |
|---|---|---|
| 1 | Getting Started | Install, first clone, API guide |
| 2 | SelfClonable | The Clone() method pattern |
| 3 | Manual Helpers | CloneSlice, CloneMap, ClonePointer |
| 4 | Struct Tags | Tag directives for code generation |
| 5 | Benchmarks | Performance data |
| 6 | API Reference | Complete function signatures |
just test # Run all tests
just test-coverage # Generate coverage report
just bench # Run all benchmarks
just all-checks # Format, vet, lint, staticcheck
just generate-nav # Update doc navigation links