自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一篇學(xué)會(huì)Go 語(yǔ)言類型可比性

開發(fā)
在 Go 中,數(shù)據(jù)類型可以被分為兩類,可比較與不可比較。兩者區(qū)分非常簡(jiǎn)單:類型是否可以使用運(yùn)算符 == 和 != 進(jìn)行比較。

[[434973]]

前段時(shí)間,一位讀者私信了我一個(gè) Go 代碼例子,并問我這是不是一個(gè) bug。我覺得蠻有意思的,故整理出了本文的分享內(nèi)容。

在討論代碼之前,讀者需要有一些前置知識(shí)。

Go 可比較類型

在 Go 中,數(shù)據(jù)類型可以被分為兩類,可比較與不可比較。兩者區(qū)分非常簡(jiǎn)單:類型是否可以使用運(yùn)算符 == 和 != 進(jìn)行比較。

那哪些類型是可比較的呢?

  • Boolean(布爾值)、Integer(整型)、Floating-point(浮點(diǎn)數(shù))、Complex(復(fù)數(shù))、String(字符)這些類型是毫無(wú)疑問可以比較的。
  • Poniter (指針) 可以比較:如果兩個(gè)指針指向同一個(gè)變量,或者兩個(gè)指針類型相同且值都為 nil,則它們相等。注意,指向不同的零大小變量的指針可能相等,也可能不相等。
  • Channel (通道)具有可比性:如果兩個(gè)通道值是由同一個(gè) make 調(diào)用創(chuàng)建的,則它們相等。
  1. c1 := make(chan int, 2) 
  2.  
  3. c2 := make(chan int, 2) 
  4.  
  5. c3 := c1 
  6.  
  7. fmt.Println(c3 == c1) // true 
  8.  
  9. fmt.Println(c2 == c1) // false 
  • Interface (接口值)具有可比性:如果兩個(gè)接口值具有相同的動(dòng)態(tài)類型和相等的動(dòng)態(tài)值,則它們相等。
  • 當(dāng)類型 X 的值具有可比性且 X 實(shí)現(xiàn) T 時(shí),非接口類型 X 的值 x 和接口類型 T 的值 t 具有可比性。如果 t 的動(dòng)態(tài)類型與 X 相同且 t 的動(dòng)態(tài)值等于 x,則它們相等。
  • 如果所有字段都具有可比性,則 struct (結(jié)構(gòu)體值)具有可比性:如果它們對(duì)應(yīng)的非空字段相等,則兩個(gè)結(jié)構(gòu)體值相等。
  • 如果 array(數(shù)組)元素類型的值是可比較的,則數(shù)組值是可比較的:如果它們對(duì)應(yīng)的元素相等,則兩個(gè)數(shù)組值相等。

哪些類型是不可比較的?

  • slice、map、function 這些是不可以比較的,但是也有特殊情況,那就是當(dāng)他們值是 nil 時(shí),可以與 nil 進(jìn)行比較。

動(dòng)態(tài)類型

在上文接口可比性中,我們提到了動(dòng)態(tài)類型與動(dòng)態(tài)值,這里需要介紹一下。

常規(guī)變量(非接口)的類型是由聲明所定義,這是靜態(tài)類型,例如 var x int。

接口類型的變量有一個(gè)獨(dú)特的動(dòng)態(tài)類型,它是運(yùn)行時(shí)存儲(chǔ)在變量中的值的實(shí)際類型。動(dòng)態(tài)類型在執(zhí)行過程中可能會(huì)有所不同,但始終可以分配給接口變量一個(gè)靜態(tài)類型。

例如

  1. var someVariable interface{} = 101 

someVariable 變量的靜態(tài)類型是 interface{},但是它的動(dòng)態(tài)類型是 int,并且很可能在之后發(fā)生變化。

  1. var someVariable interface{} = 101 
  2. someVariable = 'Gopher' 

如上, someVariable 變量的動(dòng)態(tài)類型從 int 變?yōu)榱?string。

代碼場(chǎng)景示例

我們?yōu)楫?dāng)前業(yè)務(wù)所需的數(shù)據(jù)模型定義一個(gè)結(jié)構(gòu)體 Data,它包含兩個(gè)字段:一個(gè) string 類型的 UUID 和 interface{} 類型的 Content。

  1. type Data struct { 
  2.  UUID    string 
  3.  Content interface{} 

根據(jù)上文介紹, string 類型和 interface 是可比較類型,那么兩個(gè) Data 類型的數(shù)據(jù),我們可以通過 == 操作符進(jìn)行比較。

  1. var x, y Data 
  2. x = Data{ 
  3.  UUID:    "856f5555806443e98b7ed04c5a9d6a9a"
  4.  Content: 1, 
  5. y = Data{ 
  6.  UUID:    "745dee7719304991862e6985ea9c02a9"
  7.  Content: 2, 
  8. fmt.Println(x == y) 

但是,如果在 Content 中的動(dòng)態(tài)類型是 map 會(huì)怎樣?

  1. var m, n Data 
  2. m = Data{ 
  3.  UUID:    "9584dba3fe26418d86252d71a5d78049"
  4.  Content: map[int]string{1: "GO", 2: "Python"}, 
  5. n = Data{ 
  6.  UUID:    "9584dba3fe26418d86252d71a5d78049"
  7.  Content: map[int]string{1: "GO", 2: "Python"}, 
  8. fmt.Println(m == n) 

此時(shí),我們程序編譯通過,但會(huì)發(fā)生運(yùn)行時(shí)錯(cuò)誤。

  1. panic: runtime error: comparing uncomparable type map[int]string 

那針對(duì)這種需求:即對(duì)于不可比較類型,因?yàn)椴荒苁褂帽容^操作符 ==,但我們想要比較它們包含的值是否相等時(shí),應(yīng)該怎么辦。

此時(shí)我們可以采用 reflect.DeepEqual 函數(shù)進(jìn)行比較,即將上述的 m==n 替換

  1. fmt.Println(reflect.DeepEqual(m,n)) // true 

我們得出結(jié)論:如果我們的變量中包含不可比較類型,或者 interface 類型(它的動(dòng)態(tài)類型可能存在不可比較的情況),那么我們直接運(yùn)用比較運(yùn)算符 == ,會(huì)引發(fā)程序錯(cuò)誤。此時(shí)應(yīng)該選用 reflect.DeepEqual 函數(shù)(當(dāng)然也有特殊情況,例如 []byte,可以通過 bytes. Equal 函數(shù)進(jìn)行比較)。

Bug 代碼?

好,鋪墊了這么久,終于可以展示讀者給我的代碼了。

  1. var x, y Data 
  2. x = Data{ 
  3.  UUID:    "856f5555806443e98b7ed04c5a9d6a9a"
  4.  Content: 1, 
  5. bytes, _ := json.Marshal(x) 
  6. _ = json.Unmarshal(bytes, &y) 
  7. fmt.Println(x)  // {856f5555806443e98b7ed04c5a9d6a9a 1} 
  8. fmt.Println(y)  // {856f5555806443e98b7ed04c5a9d6a9a 1} 
  9. fmt.Println(reflect.DeepEqual(x, y)) // false 

對(duì)于同一個(gè)原始數(shù)據(jù),經(jīng)過 json 的 Marshal 和 Unmarshal 過程后,竟然不相等了?難道有 bug?

不慌,這種時(shí)候,我們直接上調(diào)試看看。

debug

原來(lái)此 1 非彼 1,Content 字段的數(shù)據(jù)類型由 int 轉(zhuǎn)換為了 float64 。而在接口中,其動(dòng)態(tài)類型不一致時(shí),它的比較是不相等的。

經(jīng)過排查,發(fā)現(xiàn)問題就出在 Unmarshal 函數(shù)上:如果要將 Json 對(duì)象 Unmarshal 為接口值,那么它的類型轉(zhuǎn)換規(guī)則如下

Unmarshal

可以看到,數(shù)值型的 json 解析操作統(tǒng)一為了 float64。

因此,如果我們將 Content: 1 改為 Content: 1.0 ,那么它 reflect.DeepEqual(x, y) 的值將是 true。

增強(qiáng)型 DeepEqual 函數(shù)

針對(duì) json 解析的這種類型改變特性,我們可以基于 reflect.DeepEqual 函數(shù)進(jìn)行改造適配。

  1. func DeepEqual(v1, v2 interface{}) bool { 
  2.  if reflect.DeepEqual(v1, v2) { 
  3.   return true 
  4.  } 
  5.  bytesA, _ := json.Marshal(v1) 
  6.  bytesB, _ := json.Marshal(v2) 
  7.  return bytes.Equal(bytesA, bytesB) 

當(dāng)我們使用增強(qiáng)后的函數(shù)來(lái)運(yùn)行上述的 “bug” 例子

  1. var x, y Data 
  2. x = Data{ 
  3.  UUID:    "856f5555806443e98b7ed04c5a9d6a9a"
  4.  Content: 1, 
  5. b, _ := json.Marshal(x) 
  6. _ = json.Unmarshal(b, &y) 
  7. fmt.Println(DeepEqual(x, y)) // true 

此時(shí),結(jié)果符合我們的預(yù)期。

結(jié)論

本文討論了 Go 的可比較與不可比較類型,并對(duì)靜態(tài)、動(dòng)態(tài)類型進(jìn)行了闡述。

不可比較類型包括 slice、map、function,它們不能使用 == 進(jìn)行比較。雖然我們可以通過 == 操作符對(duì) interface 進(jìn)行比較,由于動(dòng)態(tài)類型的存在,如果實(shí)現(xiàn) interface 的 T 有不可比較類型,這將引起運(yùn)行時(shí)錯(cuò)誤。

在不能確定 interface 的實(shí)現(xiàn)類型的情況下,對(duì) interface 的比較,可以使用 reflect.DeepEqual 函數(shù)。

最后,我們通過 json 庫(kù)的解析與反解析過程中,發(fā)現(xiàn)了 json 解析存在數(shù)據(jù)類型轉(zhuǎn)換操作。這一個(gè)細(xì)節(jié),讀者在使用過程中需要注意,以免產(chǎn)生想法“這代碼有 bug” 。

參考

https://golang.org/ref/spec#Comparison_operators

https://golang.org/ref/spec#Types

https://pkg.go.dev/encoding/json#Unmarshal

 

責(zé)任編輯:武曉燕 來(lái)源: Golang技術(shù)分享
相關(guān)推薦

2023-12-05 07:14:27

AIGo

2021-07-16 22:43:10

Go并發(fā)Golang

2022-05-17 08:02:55

GoTryLock模式

2012-04-16 09:34:54

IBM思科Orac

2021-12-09 07:13:25

C#集合類型

2022-06-09 08:41:17

Go網(wǎng)絡(luò)庫(kù)Gnet

2022-04-26 09:01:39

實(shí)用工具類型TypeScript

2024-05-10 08:15:32

go語(yǔ)言反射機(jī)制

2020-10-23 08:38:19

Go語(yǔ)言

2021-10-09 07:10:31

Go語(yǔ)言基礎(chǔ)

2022-01-02 08:43:46

Python

2022-02-07 11:01:23

ZooKeeper

2021-06-24 06:35:00

Go語(yǔ)言進(jìn)程

2021-10-16 10:17:51

Go語(yǔ)言數(shù)據(jù)類型

2021-10-26 22:41:09

鴻蒙安卓系統(tǒng)

2021-04-30 09:04:11

Go 語(yǔ)言結(jié)構(gòu)體type

2021-07-02 08:51:29

源碼參數(shù)Thread

2021-09-28 08:59:30

復(fù)原IP地址

2022-04-12 08:30:52

回調(diào)函數(shù)代碼調(diào)試

2022-10-20 07:39:26

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)