<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      為什么 Go 不支持 []T 轉換為 []interface

      在 Go 中,如果 interface{} 作為函數參數的話,是可以傳任意參數的,然后通過類型斷言來轉換。

      舉個例子:

      package main
      
      import "fmt"
      
      func foo(v interface{}) {
          if v1, ok1 := v.(string); ok1 {
              fmt.Println(v1)
          } else if v2, ok2 := v.(int); ok2 {
              fmt.Println(v2)
          }
      }
      
      func main() {
          foo(233)
          foo("666")
      }
      

      不管是傳 int 還是 string,最終都能輸出正確結果。

      那么,既然是這樣的話,我就有一個疑問了,拿出我舉一反三的能力。是否可以將 []T 轉換為 []interface 呢?

      比如下面這段代碼:

      func foo([]interface{}) { /* do something */ }
      
      func main() {
          var a []string = []string{"hello", "world"}
          foo(a)
      }
      

      很遺憾,這段代碼是不能編譯通過的,如果想直接通過 b := []interface{}(a) 的方式來轉換,還是會報錯:

      cannot use a (type []string) as type []interface {} in function argument
      

      正確的轉換方式需要這樣寫:

      b := make([]interface{}, len(a), len(a))
      for i := range a {
          b[i] = a[i]
      }
      

      本來一行代碼就能搞定的事情,卻非要讓人寫四行,是不是感覺很麻煩?那為什么 Go 不支持呢?我們接著往下看。

      官方解釋

      這個問題在官方 Wiki 中是有回答的,我復制出來放在下面:

      The first is that a variable with type []interface{} is not an interface! It is a slice whose element type happens to be interface{}. But even given this, one might say that the meaning is clear.
      Well, is it? A variable with type []interface{} has a specific memory layout, known at compile time.
      Each interface{} takes up two words (one word for the type of what is contained, the other word for either the contained data or a pointer to it). As a consequence, a slice with length N and with type []interface{} is backed by a chunk of data that is N*2 words long.
      This is different than the chunk of data backing a slice with type []MyType and the same length. Its chunk of data will be N*sizeof(MyType) words long.
      The result is that you cannot quickly assign something of type []MyType to something of type []interface{}; the data behind them just look different.

      大概意思就是說,主要有兩方面原因:

      1. []interface{} 類型并不是 interface,它是一個切片,只不過碰巧它的元素是 interface
      2. []interface{} 是有特殊內存布局的,跟 interface 不一樣。

      下面就來詳細說說,是怎么個不一樣。

      內存布局

      首先來看看 slice 在內存中是如何存儲的。在源碼中,它是這樣定義的:

      // src/runtime/slice.go
      
      type slice struct {
          array unsafe.Pointer
          len   int
          cap   int
      }
      
      • array 是指向底層數組的指針;
      • len 是切片的長度;
      • cap 是切片的容量,也就是 array 數組的大小。

      舉個例子,創建如下一個切片:

      is := []int64{0x55, 0x22, 0xab, 0x9}
      

      那么它的布局如下圖所示:

      假設程序運行在 64 位的機器上,那么每個「正方形」所占空間是 8 bytes。上圖中的 ptr 所指向的底層數組占用空間就是 4 個「正方形」,也就是 32 bytes。

      接下來再看看 []interface{} 在內存中是什么樣的。

      回答這個問題之前先看一下 interface{} 的結構,Go 中的接口類型分成兩類:

      1. iface 表示包含方法的接口;
      2. eface 表示不包含方法的空接口。

      源碼中的定義分別如下:

      type iface struct {
          tab  *itab
          data unsafe.Pointer
      }
      
      type eface struct {
          _type *_type
          data  unsafe.Pointer
      }
      

      具體細節我們不去深究,但可以明確的是,每個 interface{} 包含兩個指針, 會占據兩個「正方形」。第一個指針指向 itab 或者 _type;第二個指針指向實際的數據。

      所以它在內存中的布局如下圖所示:

      因此,不能直接將 []int64 直接傳給 []interface{}

      程序運行中的內存布局

      接下來換一個更形象的方式,從程序實際運行過程中,看看內存的分布是怎么樣的?

      看下面這樣一段代碼:

      package main
      
      var sum int64
      
      func addUpDirect(s []int64) {
      	for i := 0; i < len(s); i++ {
      		sum += s[i]
      	}
      }
      
      func addUpViaInterface(s []interface{}) {
      	for i := 0; i < len(s); i++ {
      		sum += s[i].(int64)
      	}
      }
      
      func main() {
      	is := []int64{0x55, 0x22, 0xab, 0x9}
      
      	addUpDirect(is)
      
      	iis := make([]interface{}, len(is))
      	for i := 0; i < len(is); i++ {
      		iis[i] = is[i]
      	}
      
      	addUpViaInterface(iis)
      }
      

      我們使用 Delve 來進行調試,可以點擊這里進行安裝。

      dlv debug slice-layout.go
      Type 'help' for list of commands.
      (dlv) break slice-layout.go:27
      Breakpoint 1 set at 0x105a3fe for main.main() ./slice-layout.go:27
      (dlv) c
      > main.main() ./slice-layout.go:27 (hits goroutine(1):1 total:1) (PC: 0x105a3fe)
          22:		iis := make([]interface{}, len(is))
          23:		for i := 0; i < len(is); i++ {
          24:			iis[i] = is[i]
          25:		}
          26:
      =>  27:		addUpViaInterface(iis)
          28:	}
      

      打印 is 的地址:

      (dlv) p &is
      (*[]int64)(0xc00003a740)
      

      接下來看看 slice 在內存中都包含了哪些內容:

      (dlv) x -fmt hex -len 32 0xc00003a740
      0xc00003a740:   0x10   0xa7   0x03   0x00   0xc0   0x00   0x00   0x00
      0xc00003a748:   0x04   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a750:   0x04   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a758:   0x00   0x00   0x09   0x00   0xc0   0x00   0x00   0x00
      

      每行有 8 個字節,也就是上文說的一個「正方形」。第一行是指向數據的地址;第二行是 4,表示切片長度;第三行也是 4,表示切片容量。

      再來看看指向的數據到底是怎么存的:

      (dlv) x -fmt hex -len 32 0xc00003a710
      0xc00003a710:   0x55   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a718:   0x22   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a720:   0xab   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a728:   0x09   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      

      這就是一片連續的存儲空間,保存著實際數據。

      接下來用同樣的方式,再來看看 iis 的內存布局。

      (dlv) p &iis
      (*[]interface {})(0xc00003a758)
      (dlv) x -fmt hex -len 32 0xc00003a758
      0xc00003a758:   0x00   0x00   0x09   0x00   0xc0   0x00   0x00   0x00
      0xc00003a760:   0x04   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a768:   0x04   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      0xc00003a770:   0xd0   0xa7   0x03   0x00   0xc0   0x00   0x00   0x00
      

      切片的布局和 is 是一樣的,主要的不同是所指向的數據:

      (dlv) x -fmt hex -len 64 0xc000090000
      0xc000090000:   0x00   0xe4   0x05   0x01   0x00   0x00   0x00   0x00
      0xc000090008:   0xa8   0xee   0x0a   0x01   0x00   0x00   0x00   0x00
      0xc000090010:   0x00   0xe4   0x05   0x01   0x00   0x00   0x00   0x00
      0xc000090018:   0x10   0xed   0x0a   0x01   0x00   0x00   0x00   0x00
      0xc000090020:   0x00   0xe4   0x05   0x01   0x00   0x00   0x00   0x00
      0xc000090028:   0x58   0xf1   0x0a   0x01   0x00   0x00   0x00   0x00
      0xc000090030:   0x00   0xe4   0x05   0x01   0x00   0x00   0x00   0x00
      0xc000090038:   0x48   0xec   0x0a   0x01   0x00   0x00   0x00   0x00
      

      仔細觀察上面的數據,偶數行內容都是相同的,這個是 interface{}itab 地址。奇數行內容是不同的,指向實際的數據。

      打印地址內容:

      (dlv) x -fmt hex -len 8 0x010aeea8
      0x10aeea8:   0x55   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      (dlv) x -fmt hex -len 8 0x010aed10
      0x10aed10:   0x22   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      (dlv) x -fmt hex -len 8 0x010af158
      0x10af158:   0xab   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      (dlv) x -fmt hex -len 8 0x010aec48
      0x10aec48:   0x09   0x00   0x00   0x00   0x00   0x00   0x00   0x00
      

      很明顯,通過打印程序運行中的狀態,和我們的理論分析是一致的。

      通用方法

      通過以上分析,我們知道了不能轉換的原因,那有沒有一個通用方法呢?因為我實在是不想每次多寫那幾行代碼。

      也是有的,用反射 reflect,但是缺點也很明顯,效率會差一些,不建議使用。

      func InterfaceSlice(slice interface{}) []interface{} {
      	s := reflect.ValueOf(slice)
      	if s.Kind() != reflect.Slice {
      		panic("InterfaceSlice() given a non-slice type")
      	}
      
      	// Keep the distinction between nil and empty slice input
      	if s.IsNil() {
      		return nil
      	}
      
      	ret := make([]interface{}, s.Len())
      
      	for i := 0; i < s.Len(); i++ {
      		ret[i] = s.Index(i).Interface()
      	}
      
      	return ret
      }
      

      還有其他方式嗎?答案就是 Go 1.18 支持的泛型,這里就不過多介紹了,大家有興趣的話可以繼續研究。

      以上就是本文的全部內容,如果覺得還不錯的話歡迎點贊轉發關注,感謝支持。

      微信搜索「AlwaysBeta」,第一時間獲取文章更新。


      參考文章:

      推薦閱讀:

      posted @ 2023-01-30 12:41  yongxinz  閱讀(322)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 不卡一区二区国产精品| 少妇爽到呻吟的视频| 久久精品无码免费不卡| 国产在线精品一区二区三区直播 | 国产精品+日韩精品+在线播放| 好男人官网资源在线观看| 久久自己只精产国品| 亚洲国产精品无码一区二区三区 | 怡红院一区二区三区在线| 亚洲国产性夜夜综合| 久久午夜色播影院| 安康市| 国产午夜福利不卡在线观看| 亚洲人成电影在线天堂色| 酒店大战丝袜高跟鞋人妻| 亚洲国产综合精品2020| 亚洲精品一区二区三区不| 欧美视频精品免费覌看| 亚洲av无码片在线播放| 无人去码一码二码三码区| 人妻夜夜爽天天爽三区丁香花| 国内精品自线在拍| 免费特黄夫妻生活片| 116美女极品a级毛片| 国产一区二区不卡在线| 中年国产丰满熟女乱子正在播放| 日韩人妻无码精品久久| 国产精品成人中文字幕| 精品国产不卡在线观看免费| 国产伦一区二区三区久久| 精品中文人妻中文字幕| 国产精品无遮挡猛进猛出| 国产视色精品亚洲一区二区| 五月综合网亚洲乱妇久久| 日韩中文字幕在线不卡一区| 成av人电影在线观看| 漂亮的保姆hd完整版免费韩国 | 给我中国免费播放片在线| 国产目拍亚洲精品二区| 国产亚洲精品第一综合麻豆| 人妻中文字幕精品系列|