- True deep copy
- Very fast (see benchmarks section)
- Ability to copy almost all Go types (number, string, bool, function, slice, map, struct)
- Ability to copy data between convertible types (for example: copy from
inttofloat) - Ability to copy between
pointersandvalues(for example: copy from*inttoint) - Ability to copy struct fields via struct methods
- Ability to copy inherited fields from embedded structs
- Ability to set a destination struct field as
nilif it iszero - Ability to copy unexported struct fields
- Ability to configure extra copying behaviors
go get github.com/tiendc/go-deepcopy- First example
- Copy between struct fields with different names
- Skip copying struct fields
- Copy struct fields via struct methods
- Copy inherited fields from embedded structs
- Set destination struct fields as
nilonzero - PostCopy event method for structs
- Copy unexported struct fields
- Configure extra copying behaviors
type SS struct {
B bool
}
type S struct {
I int
U uint
St string
V SS
}
type DD struct {
B bool
}
type D struct {
I int
U uint
X string
V DD
}
src := []S{{I: 1, U: 2, St: "3", V: SS{B: true}}, {I: 11, U: 22, St: "33", V: SS{B: false}}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {I:1 U:2 X: V:{B:true}}
// {I:11 U:22 X: V:{B:false}} type S struct {
X int `copy:"Key"` // 'Key' is used to match the fields
U uint
St string
}
type D struct {
Y int `copy:"Key"`
U uint
}
src := []S{{X: 1, U: 2, St: "3"}, {X: 11, U: 22, St: "33"}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {Y:1 U:2}
// {Y:11 U:22}-
By default, matching fields will be copied. If you don't want to copy a field, use tag value
-.
// S and D both have `I` field, but we don't want to copy it
// Tag `-` can be used in both struct definitions or just in one
type S struct {
I int
U uint
St string
}
type D struct {
I int `copy:"-"`
U uint
}
src := []S{{I: 1, U: 2, St: "3"}, {I: 11, U: 22, St: "33"}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {I:0 U:2}
// {I:0 U:22}-
Note: If a copying method is defined within a struct, it will have higher priority than matching fields.
type S struct {
X int
U uint
St string
}
type D struct {
x string
U uint
}
// Copy method should be in form of `Copy<source-field>` (or key) and return `error` type
func (d *D) CopyX(i int) error {
d.x = fmt.Sprintf("%d", i)
return nil
} src := []S{{X: 1, U: 2, St: "3"}, {X: 11, U: 22, St: "33"}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {x:1 U:2}
// {x:11 U:22}-
This is default behaviour from v1, for lower versions, you can use custom copying function to achieve the same result.
type SBase struct {
St string
}
// Source struct has an embedded one
type S struct {
SBase
I int
}
// but destination struct doesn't
type D struct {
I int
St string
}
src := []S{{I: 1, SBase: SBase{"abc"}}, {I: 11, SBase: SBase{"xyz"}}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {I:1 St:abc}
// {I:11 St:xyz}- This is a new feature from v1.5.0. This applies to destination fields of type
pointer,interface,slice, andmap. When their values are zero after copying, they will be set asnil. This is very convenient when you don't want to send something like a date of0001-01-01to client, you want to sendnullinstead.
Playground 1 / Playground 2 / Playground 3
// Source struct has a time.Time field
type S struct {
I int
Time time.Time
}
// Destination field must be a nullable value such as `*time.Time` or `interface{}`
type D struct {
I int
Time *time.Time `copy:",nilonzero"` // make sure to use this tag
}
src := []S{{I: 1, Time: time.Time{}}, {I: 11, Time: time.Now()}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {I:1 Time:<nil>} (source is a zero time value, destination becomes `nil`)
// {I:11 Time:2025-02-08 12:31:11...} (source is not zero, so be the destination)-
This is a new feature from v1.5.0. If a destination struct has PostCopy() method, it will be called after copying.
type S struct {
I int
St string
}
type D struct {
I int
St string
}
// PostCopy must be defined on struct pointer, not value
func (d *D) PostCopy(src any) error {
d.I *= 2
d.St += d.St
return nil
}
src := []S{{I: 1, St: "a"}, {I: 11, St: "aa"}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {I:2 St:aa}
// {I:22 St:aaaa}-
By default, unexported struct fields will be ignored when copy. If you want to copy them, use tag attribute
required.
type S struct {
i int
U uint
St string
}
type D struct {
i int `copy:",required"`
U uint
}
src := []S{{i: 1, U: 2, St: "3"}, {i: 11, U: 22, St: "33"}}
var dst []D
_ = deepcopy.Copy(&dst, &src)
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {i:1 U:2}
// {i:11 U:22}-
Not allow to copy between
ptrtype andvalue(default isallow)
type S struct {
I int
U uint
}
type D struct {
I *int
U uint
}
src := []S{{I: 1, U: 2}, {I: 11, U: 22}}
var dst []D
err := deepcopy.Copy(&dst, &src, deepcopy.CopyBetweenPtrAndValue(false))
fmt.Println("error:", err)
// Output:
// error: ErrTypeNonCopyable: int -> *int-
Ignore ErrTypeNonCopyable, the process will not return that kind of error, but some copyings won't be performed.
type S struct {
I []int
U uint
}
type D struct {
I int
U uint
}
src := []S{{I: []int{1, 2, 3}, U: 2}, {I: []int{1, 2, 3}, U: 22}}
var dst []D
// The copy will succeed with ignoring copy of field `I`
_ = deepcopy.Copy(&dst, &src, deepcopy.IgnoreNonCopyableTypes(true))
for _, d := range dst {
fmt.Printf("%+v\n", d)
}
// Output:
// {I:0 U:2}
// {I:0 U:22}This benchmark is done on go-deepcopy v1.5.0.
BenchmarkCopy/Go-DeepCopy
BenchmarkCopy/Go-DeepCopy-10 1674967 703.8 ns/op
BenchmarkCopy/ManualCopy
BenchmarkCopy/ManualCopy-10 29601216 41.22 ns/op
BenchmarkCopy/jinzhu/copier
BenchmarkCopy/jinzhu/copier-10 134443 8895 ns/op
BenchmarkCopy/ulule/deepcopier
BenchmarkCopy/ulule/deepcopier-10 40231 29675 ns/op
BenchmarkCopy/mohae/deepcopy
BenchmarkCopy/mohae/deepcopy-10 503226 2204 ns/op
BenchmarkCopy/barkimedes/deepcopy
BenchmarkCopy/barkimedes/deepcopy-10 465763 2424 ns/op
BenchmarkCopy/mitchellh/copystructure
BenchmarkCopy/mitchellh/copystructure-10 101506 11316 ns/op
- You are welcome to make pull requests for new functions and bug fixes.