tiendc / go-deepcopy

Fast deep-copy library for Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 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 between struct fields and methods
  • Ability to copy between 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}

Ignore copying struct fields

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

    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 between struct fields and methods

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 between unexported struct fields

  • By default, unexported struct fields will be ignored when copy. If you want to copy them, use tag 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 error, but some copying 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

Benchmark code

BenchmarkCopy/Go-DeepCopy
BenchmarkCopy/Go-DeepCopy-10         	 1712484	       685.5 ns/op

BenchmarkCopy/ManualCopy
BenchmarkCopy/ManualCopy-10          	27953836	        41.14 ns/op

BenchmarkCopy/JinzhuCopier
BenchmarkCopy/JinzhuCopier-10        	  129792	      9177 ns/op

BenchmarkCopy/Deepcopier
BenchmarkCopy/Deepcopier-10          	   42990	     27988 ns/op

Contributing

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

Authors

License

About

Fast deep-copy library for Go

License:MIT License


Languages

Language:Go 99.2%Language:Makefile 0.8%