<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 語(yǔ)言的幾個(gè)結(jié)構(gòu)體騷操作

          共 5472字,需瀏覽 11分鐘

           ·

          2021-12-19 11:21

          我們知道 Go 沒(méi)有繼承的概念,接口結(jié)構(gòu)體多使用組合,很多開(kāi)源產(chǎn)品或是源代碼都有大量的內(nèi)嵌 (embeded field) 字段,用于特殊目的

          1. NoCopy

          package?main

          import?(
          ?"sync"
          )

          func?test(wg?sync.WaitGroup)?{
          ?defer?wg.Done()
          ?wg.Add(1)
          }

          func?main()?{
          ?var?wg?sync.WaitGroup
          ?wg.Add(1)
          ?go?test(wg)
          ?wg.Wait()
          }

          這是非常經(jīng)典的 case, 程序執(zhí)行報(bào)錯(cuò) all goroutines are asleep - deadlock!, 解決也很簡(jiǎn)單,把 wg 由值傳遞變成指針類型即可。本質(zhì)是 WaitGroup 內(nèi)部維護(hù)了計(jì)數(shù),不允許 copy 變量,還有 sync.Mutex 鎖也是不允許 copy 的

          解決辦法很簡(jiǎn)單,需要 CI 時(shí)由 linter 檢測(cè)出來(lái),最好運(yùn)行時(shí)也能有檢測(cè)機(jī)制,這方面的討論請(qǐng)參考issue 8005[1]

          zerun.dong$?go?vet?aaa.go
          #?command-line-arguments
          ./aaa.go:7:14:?test?passes?lock?by?value:?sync.WaitGroup?contains?sync.noCopy
          ./aaa.go:15:10:?call?of?test?copies?lock?value:?sync.WaitGroup?contains?sync.noCopy

          這是 go vet 結(jié)果,報(bào)錯(cuò)己經(jīng)很明顯了

          type?noCopy?struct{}

          noCopy 定義非常簡(jiǎn)單,空結(jié)構(gòu)體,zero size 不占用空間(前提是非結(jié)構(gòu)體的最后一個(gè)字段,否則還要是有 8 byte 空間開(kāi)銷)

          sync.WaitGroup[2] 內(nèi)嵌 noCopy 字段,防止 Cond 變量被復(fù)制

          type?WaitGroup?struct?{
          ?noCopy?noCopy

          ?//?64-bit?value:?high?32?bits?are?counter,?low?32?bits?are?waiter?count.
          ?//?64-bit?atomic?operations?require?64-bit?alignment,?but?32-bit
          ?//?compilers?only?guarantee?that?64-bit?fields?are?32-bit?aligned.
          ?//?For?this?reason?on?32?bit?architectures?we?need?to?check?in?state()
          ?//?if?state1?is?aligned?or?not,?and?dynamically?"swap"?the?field?order?if
          ?//?needed.
          ?state1?uint64
          ?state2?uint32
          }

          上面是 sync.WaitGroup 結(jié)構(gòu)體的定義,同時(shí)注意 noCopy 是源碼中不可導(dǎo)出的定義。如果用戶代碼也想實(shí)現(xiàn) NoCopy 呢?可以參考 grpc DoNotCopy[3]

          //?DoNotCopy?can?be?embedded?in?a?struct?to?help?prevent?shallow?copies.
          //?This?does?not?rely?on?a?Go?language?feature,?but?rather?a?special?case
          //?within?the?vet?checker.
          type?DoNotCopy?[0]sync.Mutex

          非常簡(jiǎn)單,Mutex 零長(zhǎng)數(shù)組,不占用空間。由于 vet checker 會(huì)檢測(cè) Mutex,相當(dāng)于替我們實(shí)現(xiàn)了 noCopy 功能

          2. DoNotCompare

          Golang Sepc Comparison_operators[4] 官方文檔描述常見(jiàn)類型比較運(yùn)算( == != > < <= >=)的結(jié)果,詳細(xì)內(nèi)容看官方文檔 https://go.dev/ref/spec#Comparison_operators

          1. In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

          2. The equality operators == and != apply to operands that are comparable. The ordering operators <, <=, >, and >= apply to operands that are ordered.

          3. Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

          4. Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil. Comparison of pointer, channel, and interface values to nil is also allowed and follows from the general rules above.

          對(duì)于 struct 來(lái)講,只有所有字段全部 comparable 的(不限大小寫(xiě)是否導(dǎo)出),那么結(jié)構(gòu)體才可以比較。同時(shí)只比較 non-blank 的字段,舉個(gè)例子:

          type?T?struct?{
          ????name?string
          ????age?int
          ????_?float64
          }
          func?main()?{
          ???x?:=?[...]float64{1.1,?2,?3.14}
          ???fmt.Println(x?==?[...]float64{1.1,?2,?3.14})?//?true
          ???y?:=?[1]T{{"foo",?1,?0}}
          ???fmt.Println(y?==?[1]T{{"foo",?1,?1}})?//?true
          }

          運(yùn)行后,結(jié)果均為 true

          Slice, Map, Function 均是不可比較的,只與判斷是否為 nil. 所以我們可以利用這兩個(gè)特性,內(nèi)嵌函數(shù)來(lái)實(shí)現(xiàn)不可比較,參考 protobuf DoNotCompare[5]

          //?DoNotCompare?can?be?embedded?in?a?struct?to?prevent?comparability.
          type?DoNotCompare?[0]func()

          如果比較會(huì)報(bào)錯(cuò)

          type?DoNotCompare?[0]func()

          type?T?struct?{
          ????name?string
          ????age?int
          ????DoNotCompare
          }
          func?main()?{
          //?./cmp.go:13:21:?invalid?operation:?T{}?==?T{}?(struct?containing?DoNotCompare?cannot?be?compared)
          ????fmt.Println(T{}?==?T{})
          }

          3. NoUnkeyedLiterals

          結(jié)構(gòu)體初始化有兩種:指定字段名稱,或者按順序列出所有字段,不指定名稱

          type?User?struct{
          ????Age?int
          ????Address?string
          }

          u?:=?&User{21,?"beijing"}

          這樣寫(xiě)的問(wèn)題非常大,如果新增字段會(huì)不兼容

          type?User?struct{
          ????Age?int
          ????Address?string
          ????Money?int
          }

          func?main(){
          //?./struct.go:11:15:?too?few?values?in?User{...}
          ??_?=?&User{21,?"beijing"}
          }

          上面的例子,能在編譯期報(bào)錯(cuò)還是可接受的,如果同類型的調(diào)換順序,那才叫坑爹... 所以這時(shí)需要 NoUnkeyedLiterals[6]

          //?NoUnkeyedLiterals?can?be?embedded?in?a?struct?to?prevent?unkeyed?literals.
          type?NoUnkeyedLiterals?struct{}

          很簡(jiǎn)單,就是一個(gè)空結(jié)構(gòu)體,這是 Protobuf 的實(shí)現(xiàn)。很多時(shí)候我們都用空的結(jié)構(gòu)體占位符實(shí)現(xiàn)

          type?User?struct{
          ????_?struct{}
          ????Age?int
          ????Address?string
          }

          func?main(){
          //?./struct.go:10:11:?cannot?use?21?(type?int)?as?type?struct?{}?in?field?value
          //?./struct.go:10:15:?cannot?use?"beijing"?(type?untyped?string)?as?type?int?in?field?value
          //?./struct.go:10:15:?too?few?values?in?User{...}
          _?=?&User{21,?"beijing"}
          }

          報(bào)錯(cuò)很明顯了,字段類型不匹配,有人會(huì)說(shuō)初始化寫(xiě)上 struct{} 不就可以了?

          _?=?&User{struct{}{},?21,?"beijing"}

          這樣確實(shí)可以工作,但是占位符 _ 的字段是不可導(dǎo)出的,所以 import 其它包的 NoUnkeyedLiterals 結(jié)構(gòu)體同樣會(huì)報(bào)錯(cuò)

          4. Copier 庫(kù)

          最后推薦一個(gè)非常實(shí)用的 copier[7] 庫(kù),CRUD Boy 經(jīng)常結(jié)構(gòu)體轉(zhuǎn)來(lái)轉(zhuǎn)去的,比如 dto, dao 互轉(zhuǎn),或是 dao 與其它互轉(zhuǎn),如果修改了 dao 結(jié)構(gòu)體,還要記得修改其它轉(zhuǎn)換邏輯,非常繁瑣

          package?main
          import?(
          ??"fmt"
          ??"github.com/jinzhu/copier"
          )

          type?User?struct?{
          ??Name?string
          ??Age??int
          }

          type?Employee?struct?{
          ??Name?string
          ??Age??int
          ??Role?string
          }

          func?main()?{
          ??user?:=?User{Name:?"dj",?Age:?18}
          ??employee?:=?Employee{Role:?"admin"}

          ??copier.Copy(&employee,?&user)
          ??//?main.Employee{Name:"dj",?Age:18,?Role:"admin"}
          ??fmt.Printf("%#v\n",?employee)
          }

          打印 Employee 發(fā)現(xiàn) name, age 字段己經(jīng)賦值了,非常好用。感興趣的可以查看官網(wǎng),支持非常多的高級(jí)玩法

          注意:這里是隱式的,有人偏好所有字段顯示賦值,大家怎么看?

          小結(jié)

          寫(xiě)文章不容易,如果對(duì)大家有所幫助和啟發(fā),請(qǐng)大家?guī)兔c(diǎn)擊在看點(diǎn)贊分享 三連


          參考資料

          [1]

          nocopy issue 8005: https://github.com/golang/go/issues/8005,

          [2]

          sync.WaitGroup noCopy: https://github.com/golang/go/blob/master/src/sync/waitgroup.go#L212,

          [3]

          pprotobuf DoNotCopy: https://pkg.go.dev/google.golang.org/[email protected]/internal/pragma#DoNotCopy,

          [4]

          官方文檔Comparison_operators: https://go.dev/ref/spec#Comparison_operators,

          [5]

          DoNotCompare: https://pkg.go.dev/google.golang.org/[email protected]/internal/pragma#DoNotCompare,

          [6]

          protobuf NoUnkeyedLiterals: https://github.com/protocolbuffers/protobuf-go/blob/v1.27.1/internal/pragma/pragma.go#L12,

          [7]

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


          ? ?


          喜歡明哥文章的同學(xué)
          歡迎長(zhǎng)按下圖訂閱!

          ???

          瀏覽 126
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亲子乱AV一区二区 | 又粗又大又黄的视频 | 天天日天天日天天日 | 无码人妻一区二区三区综合另类 | sese网站 |