Golang Copier入门到入坑探究

 更新时间:2022年11月18日 15:44:47   作者:小白要生发  
这篇文章主要为大家介绍了Golang Copier入门到入坑探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

github: https://github.com/jinzhu/copier

由于 golang 没有对复杂结构体的 clone 方法,所以,就需要有 copier 这样的工具库。

它看起来很简单,但实际使用中,有些“坑”还是要注意!

本文:

入门为辅,探“坑”为主,

看完再划走,CS我没有。

安装

go get github.com/jinzhu/copier

快速入门

好的,来一段代码快速了解 copier

package main
import (
    "fmt"
    "github.com/jinzhu/copier"
)
type SC struct {
    C uint8
}
type M1 struct {
    A int
    W string
    S *SC
}
func main() {
    var src = M1{12, "Hello", &SC{32}}
    var dst = M1{}
    fmt.Printf("before copy src %+v\tdst %+v\n", src, dst)
    copier.Copy(&dst, src)
    fmt.Printf("after  copy src %+v\tdst %+v\n", src, dst)
}

输出:

before copy src {A:12 W:Hello S:0xc00017f550}   dst {A:0 W: S:<nil>}
after  copy src {A:12 W:Hello S:0xc00017f550}   dst {A:12 W:Hello S:0xc00017f618}

好的,看到这,你就已掌握了 copier 80%的功能了。先别着急划走,接下来还是踩坑记录。

本文代码运行输出内容是基于 github.com/jinzhu/copier@v0.3.5 和 go1.16.1 darwin/amd64 环境演示的结果。

入坑

package main
import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)
type SC struct {
    C uint8
}
type Map1 struct {
    M map[string]int32
    A []int32
    C *SC
}
func main() {
    var src = Map1{map[string]int32{"C:": 3, "d": 4}, []int32{9, 8}, &SC{32}}
    var dst1 = Map1{}
    spew.Printf("before src %+v\t\tdst %+v\n", src, dst1)
    copier.Copy(&dst1, src)
    dst1.M["F"] = 5
    dst1.M["g"] = 6
    dst1.A[0] = 7
    dst1.C.C = 27
    spew.Printf("after  src %+v\tdst %+v\n", src, dst1)
}

以上代码运行后会输出:

before src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}}          dst {M:<nil> A:<nil> C:<nil>}

befre 那一行代码如上⬆️ , after 那一行会输出什么呢?

1. after  src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:27}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
2. after  src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
3. after  src {M:map[C::3 d:4] A:[7 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
4. after  src {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
5. after  src {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a1e8){C:27}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}

答案是: var a = int(759 / 6 / 31.5)

为了避免不小心看了答案,请计算 759 / 6 / 31.5 得出的值四舍五入便是。

再探坑出坑

我看其他同学使用 copier 也是像上面那样——copier.Copy($dst, src), 当然啦,也不排除我!它仿佛就是一把小巧精悍的小刀。一个简单的函数调用,就完成了它的使命。

然而,它其实是把多功能刀,我都还没有打开它的—— option

上面的问题就是,我 Copy 后,对值的改动,影响了另一个值的 map,那么这个时候,就需要进行深 copy。接下来引入 copier 的 option

package main
import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)
type SC struct {
    C uint8
}
type Map1 struct {
    M map[string]int32
    A []int32
    C *SC
}
func main() {
    var src = Map1{map[string]int32{"C:": 3, "d": 4}, []int32{9, 8}, &SC{32}}
    var dst1 = Map1{}
    spew.Printf("before src %+v\t\tdst %+v\n", src, dst1)
    copier.CopyWithOption(&dst1, src, copier.Option{DeepCopy: true})   // 这里!
    dst1.M["F"] = 5
    dst1.M["g"] = 6
    dst1.A[0] = 7
    dst1.C.C = 27
    spew.Printf("after  src %+v\tdst %+v\n", src, dst1)
}

好的,这样copy之后,对新变量的改动,不会传递会原变量改动了。

再盘一盘坑

package main
import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)
type ArrTC struct {
    Name [2]string
    C    *ArrTC
}
type ArrT struct {
    A  [3]int32
    S  []int32
    E  []int32
    C  string
    V  string
    M map[string]int32
    AC ArrTC
    s bool
}
func main() {
    var src = ArrT{
        [3]int32{9, 10, 0},
        []int32{12, 0},
        []int32{},
        "",
        "val",
        map[string]int32{"A:": 1, "b": 0},
        ArrTC{},
        true,
    }
    var dst = ArrT{
        [3]int32{1, 2, 3},
        []int32{4, 5, 6, 7},
        []int32{9, 10},
        "char",
        "ha",
        map[string]int32{"C:": 3, "b": 4, ".": 0},
        ArrTC{[2]string{"Y", "Z"}, nil},
        false,
    }
    spew.Printf("before src %+v\tdst %+v\n", src, dst)
    copier.CopyWithOption(&dst, src, copier.Option{IgnoreEmpty: true, DeepCopy: true})
    spew.Printf("after  src %+v\tdst %+v\n", src, dst)
    src.M["b"] = 99
    src.S[1] = 1
    dst.S[0] = 2
    spew.Printf("last  src %+v\tdst %+v\n\n", src, dst)
}

输出:

before src {A:[9 10 0] S:[12 0] E:[] C: V:val M:map[A::1 b:0] AC:{Name:[ ] C:<nil>} s:true}    dst {A:[1 2 3] S:[4 5 6 7] E:[9 10] C:char V:ha M:map[C::3 b:4 .:0] AC:{Name:[Y Z] C:<nil>} s:false}
after  src {A:[9 10 0] S:[12 0] E:[] C: V:val M:map[A::1 b:0] AC:{Name:[ ] C:<nil>} s:true}    dst {A:[9 10 0] S:[12 0 6 7] E:[9 10] C:char V:val M:map[A::1 C::3 b:0 .:0] AC:{Name:[Y Z] C:<nil>} s:true}
last  src {A:[9 10 0] S:[12 1] E:[] C: V:val M:map[A::1 b:99] AC:{Name:[ ] C:<nil>} s:true}    dst {A:[9 10 0] S:[2 0 6 7] E:[9 10] C:char V:val M:map[C::3 b:0 .:0 A::1] AC:{Name:[Y Z] C:<nil>} s:true}

这次的代码我加上了 IgnoreEmpty: true, 也就是复制时忽略空的值。 也就说可以当作值 merge 用。

然后,又测试了一下变量独立性。复制之后,srcdst 两个变量再无瓜葛,对其中一个值的任意改动都不会同步到另一个值。

但是,这个 merge 的表现,可能不是你想的那样,

src.S = []int32{12, 0}
dst.S = []int32{4, 5, 6, 7}
## 调用 copy 后, 你预期的结果是什么?[6/7]
6. dst.S = []int32{12, 0}
7. dst.S = []int32{12, 0, 6, 7}
  • 选项6: 嗯,原来是 {12, 0} 复制给 dst 就是 {12, 0}
  • 选项7: 这个是切片,你只给我 0,1 位的值,copier把 0,1 位置的值 copy 了,dst后面2,3位的值,src没给出,那就不管。所以就是 {12, 0, 6, 7}

这块的表现,我觉得是有争议的,大佬们在评论区留下你预期选项,看看大家是不是都这样想的。

实际运行结果,见上面的代码输出就能找到答案。

结语

copier 本来是一个短小精悍的工具库,也没想要水一篇,最近使用时,突然踩坑,就特开一篇,和大家分享一下踩坑经验。

在使用外部库的时候,还是建议去 github 上看看详细说明, 或者上 pkg.go.dev 看看它暴露出来出的接口以及说明。更或者进行完整的测试,充分了解它之后,再使用。

以上就是Golang Copier入门到入坑探究的详细内容,更多关于Golang Copier入门的资料请关注脚本之家其它相关文章!

相关文章

  • VSCode必装Go语言以下插件的思路详解

    VSCode必装Go语言以下插件的思路详解

    这篇文章主要介绍了VSCode必装Go语言以下插件的思路详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • go语言的变量定义示例详解

    go语言的变量定义示例详解

    这篇文章主要为大家介绍了go语言的变量定义示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 一篇文章带你轻松搞懂Golang的error处理

    一篇文章带你轻松搞懂Golang的error处理

    在进行后台开发的时候,错误处理是每个程序员都会遇到的问题,下面这篇文章主要给大家介绍了关于Golang中error处理的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • golang并发安全及读写互斥锁的示例分析

    golang并发安全及读写互斥锁的示例分析

    这篇文章主要为大家介绍了golang并发安全及读写互斥锁的示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • 使用go求幂的几种方法小结

    使用go求幂的几种方法小结

    这篇文章主要介绍了使用go求幂的几种方法小结,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go语言中database/sql的用法介绍

    Go语言中database/sql的用法介绍

    Go语言中的database/sql包定义了对数据库的一系列操作,database/sql/driver包定义了应被数据库驱动实现的接口,这些接口会被sql包使用,本文将详细给大家介绍Go的database/sql的使用方法,需要的朋友可以参考下
    2023-05-05
  • Go Excelize API源码解读GetSheetViewOptions与SetPageLayout

    Go Excelize API源码解读GetSheetViewOptions与SetPageLayo

    这篇文章主要为大家介绍了Go Excelize API源码解读GetSheetViewOptions与SetPageLayout方法示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Go语言中匿名嵌套和类型嵌套的区别解析

    Go语言中匿名嵌套和类型嵌套的区别解析

    在Go语言中,匿名嵌套结构体和与类型同名的嵌套结构体不是完全等价的,它们有一些重要的区别,这篇文章主要介绍了Go语言中匿名嵌套和类型嵌套的区别,需要的朋友可以参考下
    2023-09-09
  • Go语言之嵌入类型详解

    Go语言之嵌入类型详解

    本文详细讲解了Go语言之嵌入类型,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • go语言题解LeetCode1128等价多米诺骨牌对的数量

    go语言题解LeetCode1128等价多米诺骨牌对的数量

    这篇文章主要为大家介绍了go语言题解LeetCode1128等价多米诺骨牌对的数量示例详解,
    2022-12-12

最新评论