deepcopy

package module
v1.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 6, 2025 License: MIT Imports: 6 Imported by: 5

README

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

Documentation

Index

Constants

View Source
const (
	DefaultTagName = "copy"
)

Variables

View Source
var (
	// ErrTypeInvalid returned when type of input var does not meet the requirement
	ErrTypeInvalid = errors.New("ErrTypeInvalid")
	// ErrTypeNonCopyable returned when the function can not perform copying between types
	ErrTypeNonCopyable = errors.New("ErrTypeNonCopyable")
	// ErrValueInvalid returned when input value does not meet the requirement
	ErrValueInvalid = errors.New("ErrValueInvalid")
	// ErrValueUnaddressable returned when value is `unaddressable` which is required
	// in some situations such as when accessing an unexported struct field.
	ErrValueUnaddressable = errors.New("ErrValueUnaddressable")
	// ErrFieldRequireCopying returned when a field is required to be copied
	// but no copying is done for it.
	ErrFieldRequireCopying = errors.New("ErrFieldRequireCopying")
	// ErrMethodInvalid returned when copying method of a struct is not valid
	ErrMethodInvalid = errors.New("ErrMethodInvalid")
)

Errors may be returned from Copy function

Functions

func ClearCache

func ClearCache()

ClearCache clears global cache of previously used copiers

func Copy

func Copy(dst, src any, options ...Option) (err error)

Copy performs deep copy from `src` to `dst`.

`dst` must be a pointer to the output var, `src` can be either value or pointer. In case you want to copy unexported struct fields within `src`, `src` must be a pointer.

func SetDefaultTagName added in v1.1.0

func SetDefaultTagName(tag string)

SetDefaultTagName overwrites the default tag name. This function should only be called once at program startup.

Types

type Context

type Context struct {
	// CopyBetweenPtrAndValue allow or not copying between pointers and values (default is `true`)
	CopyBetweenPtrAndValue bool

	// CopyBetweenStructFieldAndMethod allow or not copying between struct fields and methods (default is `true`)
	CopyBetweenStructFieldAndMethod bool

	// IgnoreNonCopyableTypes ignore non-copyable types (default is `false`)
	IgnoreNonCopyableTypes bool

	// UseGlobalCache if false not use global cache (default is `true`)
	UseGlobalCache bool
	// contains filtered or unexported fields
}

Context copier context

type Option

type Option func(ctx *Context)

Option configuration option function provided as extra arguments of copying function

func CopyBetweenPtrAndValue

func CopyBetweenPtrAndValue(flag bool) Option

CopyBetweenPtrAndValue config function for setting flag `CopyBetweenPtrAndValue`

func CopyBetweenStructFieldAndMethod

func CopyBetweenStructFieldAndMethod(flag bool) Option

CopyBetweenStructFieldAndMethod config function for setting flag `CopyBetweenStructFieldAndMethod`

func IgnoreNonCopyableTypes

func IgnoreNonCopyableTypes(flag bool) Option

IgnoreNonCopyableTypes config function for setting flag `IgnoreNonCopyableTypes`

func UseGlobalCache

func UseGlobalCache(flag bool) Option

UseGlobalCache config function for setting flag `UseGlobalCache`

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL