GitHub - tiendc/go-deepcopy: Fast deep-copy library for Go (original) (raw)

Go Version GoDoc Build Status Coverage Status GoReport

Fast deep-copy library for Go

Functionalities

Installation

go get github.com/tiendc/go-deepcopy

Usage

First example

Playground

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) // NOTE: it is recommended that you always pass address of `src` to the function
                              // when copy structs having unexported fields such as `time.Time`.
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}}

Copy between struct fields with different names

Playground

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}

Skip copying struct fields

Require copying for struct fields

Playground

type S struct {
    U  uint
    St string
}
type D struct {
    I int `copy:",required"`
    U uint
}
src := []S{{U: 2, St: "3"}, {U: 22, St: "33"}}
var dst []D
err := deepcopy.Copy(&dst, &src)
if err != nil {
    fmt.Println("error:", err)
}

// Output:
// error: ErrFieldRequireCopying: struct field 'main.D[I]' requires copying

Copy struct fields via struct methods

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}

Copy inherited fields from embedded structs

Set destination struct fields as nil on zero

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)

PostCopy event method for structs

Copy between structs and maps

Playground

type D struct {
    I int  `copy:"i"`
    U uint `copy:"u"`
}

src := map[string]any{"i": 1, "u": 2, "s": "abc"}
var dst D
err := deepcopy.Copy(&dst, &src)
if err != nil {
    fmt.Println("error:", err)
}
fmt.Printf("Result struct: %+v\n", dst)

src2 := D{I: 11, U: 22}
dst2 := map[string]any{}
err = deepcopy.Copy(&dst2, &src2)
if err != nil {
    fmt.Println("error:", err)
}
fmt.Printf("Result map: %+v\n", dst2)

// Output:
// Result struct: {I:1 U:2}
// Result map: map[i:11 u:22]

Configure extra copying behaviors

Benchmarks

Go-DeepCopy vs ManualCopy vs Other Libs

This benchmark is done on go-deepcopy v1.6.0 using Go 1.24.2

Copy between 2 different struct types Benchmark code

Go-DeepCopy
Go-DeepCopy-10         	 1734240	       682.3 ns/op	     344 B/op	       4 allocs/op
ManualCopy
ManualCopy-10          	32983267	        35.59 ns/op	      80 B/op	       1 allocs/op
JinzhuCopier
JinzhuCopier-10        	  146428	      8138 ns/op	     912 B/op	      40 allocs/op
ulule/deepcopier
ulule/deepcopier-10    	   47137	     25717 ns/op	   50752 B/op	     550 allocs/op
mohae/deepcopy
mohae/deepcopy-10      	  597488	      1828 ns/op	    1208 B/op	      42 allocs/op
barkimedes/deepcopy
barkimedes/deepcopy-10 	  528232	      2206 ns/op	    1464 B/op	      15 allocs/op
mitchellh/copystructure
mitchellh/copystructure-10   123484	      9545 ns/op	    7592 B/op	     191 allocs/op

Copy between the same struct type Benchmark code

Go-DeepCopy
Go-DeepCopy-10         	 2693502	       438.0 ns/op	     248 B/op	       4 allocs/op
ManualCopy
ManualCopy-10          	36577604	        32.17 ns/op	      80 B/op	       1 allocs/op
JinzhuCopier
JinzhuCopier-10        	  187950	      6468 ns/op	     824 B/op	      39 allocs/op
ulule/deepcopier
ulule/deepcopier-10    	   50193	     23170 ns/op	   44768 B/op	     486 allocs/op
mohae/deepcopy
mohae/deepcopy-10      	 1000000	      1083 ns/op	     640 B/op	      26 allocs/op
barkimedes/deepcopy
barkimedes/deepcopy-10 	  886911	      1345 ns/op	     888 B/op	      13 allocs/op
mitchellh/copystructure
mitchellh/copystructure-10   212216	      5559 ns/op	    4552 B/op	     116 allocs/op

Contributing

License