Golang Copier入門(mén)到入坑探究
正文
github: https://github.com/jinzhu/copier
由于 golang 沒(méi)有對(duì)復(fù)雜結(jié)構(gòu)體的 clone 方法,所以,就需要有 copier 這樣的工具庫(kù)。
它看起來(lái)很簡(jiǎn)單,但實(shí)際使用中,有些“坑”還是要注意!
本文:
入門(mén)為輔,探“坑”為主,
看完再劃走,CS我沒(méi)有。
安裝
go get github.com/jinzhu/copier
快速入門(mén)
好的,來(lái)一段代碼快速了解 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%的功能了。先別著急劃走,接下來(lái)還是踩坑記錄。
本文代碼運(yùn)行輸出內(nèi)容是基于 github.com/jinzhu/copier@v0.3.5 和 go1.16.1 darwin/amd64 環(huán)境演示的結(jié)果。
入坑
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)
}以上代碼運(yùn)行后會(huì)輸出:
before src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}} dst {M:<nil> A:<nil> C:<nil>}
befre 那一行代碼如上?? , after 那一行會(huì)輸出什么呢?
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)
為了避免不小心看了答案,請(qǐng)計(jì)算 759 / 6 / 31.5 得出的值四舍五入便是。
再探坑出坑
我看其他同學(xué)使用 copier 也是像上面那樣——copier.Copy($dst, src), 當(dāng)然啦,也不排除我!它仿佛就是一把小巧精悍的小刀。一個(gè)簡(jiǎn)單的函數(shù)調(diào)用,就完成了它的使命。
然而,它其實(shí)是把多功能刀,我都還沒(méi)有打開(kāi)它的—— option。
上面的問(wèn)題就是,我 Copy 后,對(duì)值的改動(dòng),影響了另一個(gè)值的 map,那么這個(gè)時(shí)候,就需要進(jìn)行深 copy。接下來(lái)引入 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之后,對(duì)新變量的改動(dòng),不會(huì)傳遞會(huì)原變量改動(dòng)了。
再盤(pán)一盤(pán)坑
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, 也就是復(fù)制時(shí)忽略空的值。 也就說(shuō)可以當(dāng)作值 merge 用。
然后,又測(cè)試了一下變量獨(dú)立性。復(fù)制之后,src, dst 兩個(gè)變量再無(wú)瓜葛,對(duì)其中一個(gè)值的任意改動(dòng)都不會(huì)同步到另一個(gè)值。
但是,這個(gè) merge 的表現(xiàn),可能不是你想的那樣,
src.S = []int32{12, 0}
dst.S = []int32{4, 5, 6, 7}
## 調(diào)用 copy 后, 你預(yù)期的結(jié)果是什么?[6/7]
6. dst.S = []int32{12, 0}
7. dst.S = []int32{12, 0, 6, 7}- 選項(xiàng)6: 嗯,原來(lái)是 {12, 0} 復(fù)制給 dst 就是 {12, 0}
- 選項(xiàng)7: 這個(gè)是切片,你只給我 0,1 位的值,copier把 0,1 位置的值
copy了,dst后面2,3位的值,src沒(méi)給出,那就不管。所以就是 {12, 0, 6, 7}
這塊的表現(xiàn),我覺(jué)得是有爭(zhēng)議的,大佬們?cè)谠u(píng)論區(qū)留下你預(yù)期選項(xiàng),看看大家是不是都這樣想的。
實(shí)際運(yùn)行結(jié)果,見(jiàn)上面的代碼輸出就能找到答案。
結(jié)語(yǔ)
copier 本來(lái)是一個(gè)短小精悍的工具庫(kù),也沒(méi)想要水一篇,最近使用時(shí),突然踩坑,就特開(kāi)一篇,和大家分享一下踩坑經(jīng)驗(yàn)。
在使用外部庫(kù)的時(shí)候,還是建議去 github 上看看詳細(xì)說(shuō)明, 或者上 pkg.go.dev 看看它暴露出來(lái)出的接口以及說(shuō)明。更或者進(jìn)行完整的測(cè)試,充分了解它之后,再使用。
以上就是Golang Copier入門(mén)到入坑探究的詳細(xì)內(nèi)容,更多關(guān)于Golang Copier入門(mén)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang進(jìn)行簡(jiǎn)單權(quán)限認(rèn)證的實(shí)現(xiàn)
本文主要介紹了golang簡(jiǎn)單權(quán)限認(rèn)證的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
一些關(guān)于Go程序錯(cuò)誤處理的相關(guān)建議
錯(cuò)誤處理在每個(gè)語(yǔ)言中都是一項(xiàng)重要內(nèi)容,眾所周知,通常寫(xiě)程序時(shí)遇到的分為異常與錯(cuò)誤兩種,Golang中也不例外,這篇文章主要給大家介紹了一些關(guān)于Go程序錯(cuò)誤處理的相關(guān)建議,需要的朋友可以參考下2021-09-09
go語(yǔ)言中結(jié)構(gòu)體tag使用小結(jié)
Go語(yǔ)言是一種靜態(tài)類型、編譯型的編程語(yǔ)言,其中結(jié)構(gòu)體是一種非常重要的數(shù)據(jù)類型,本文就來(lái)介紹一下go語(yǔ)言中結(jié)構(gòu)體tag使用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
Go 循環(huán)結(jié)構(gòu)for循環(huán)使用教程全面講解
這篇文章主要為大家介紹了Go 循環(huán)結(jié)構(gòu)for循環(huán)使用全面講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
Go語(yǔ)言實(shí)現(xiàn)類似c++中的多態(tài)功能實(shí)例
Go本身不具有多態(tài)的特性,不能夠像Java、C++那樣編寫(xiě)多態(tài)類、多態(tài)方法。但是,使用Go可以編寫(xiě)具有多態(tài)功能的類綁定的方法。下面來(lái)一起看看吧2016-09-09

