Skip to content

tiendc/go-deepcopy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go Version GoDoc Build Status Coverage Status GoReport

Fast deep-copy library for Go

Functionalities

  • 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 int to float)
  • Ability to copy between pointers and values (for example: copy from *int to int)
  • Ability to copy struct fields via struct methods
  • Ability to copy inherited fields from embedded structs
  • Ability to copy unexported struct fields
  • Ability to configure copying behavior

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)

    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

  • By default, matching fields will be copied. If you don't want to copy a field, use tag value -.

    Playground

    // 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}

Copy struct fields via struct methods

  • Note: If a copying method is defined within a struct, it will have higher priority than matching fields.

    Playground 1 / Playground 2

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

  • This is default behaviour from version 1.0, for lower versions, you can use custom copying function to achieve the same result.

    Playground 1 / Playground 2

    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}

Copy unexported struct fields

  • By default, unexported struct fields will be ignored when copy. If you want to copy them, use tag attribute required.

    Playground

    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}

Configure copying behavior

  • Not allow to copy between ptr type and value (default is allow)

    Playground

    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.

    Playground 1 / Playground 2

    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}

Benchmarks

Go-DeepCopy vs ManualCopy vs JinzhuCopier vs Deepcopier

This is the benchmark result of the latest version of the lib.

Benchmark code

BenchmarkCopy/Go-DeepCopy
BenchmarkCopy/Go-DeepCopy-10         	 1737516	       691.4 ns/op
BenchmarkCopy/ManualCopy
BenchmarkCopy/ManualCopy-10          	25902331	        41.14 ns/op
BenchmarkCopy/jinzhu/copier
BenchmarkCopy/jinzhu/copier-10       	  133630	      8929 ns/op
BenchmarkCopy/ulule/deepcopier
BenchmarkCopy/ulule/deepcopier-10    	   39864	     29785 ns/op
BenchmarkCopy/mohae/deepcopy
BenchmarkCopy/mohae/deepcopy-10      	  491649	      2227 ns/op
BenchmarkCopy/barkimedes/deepcopy
BenchmarkCopy/barkimedes/deepcopy-10 	  483442	      2450 ns/op
BenchmarkCopy/mitchellh/copystructure
BenchmarkCopy/mitchellh/copystructure-10   99501	     11379 ns/op

Contributing

  • You are welcome to make pull requests for new functions and bug fixes.

License