最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go?不支持?[]T轉(zhuǎn)換為[]interface類型詳解

 更新時(shí)間:2023年01月31日 09:03:49   作者:yongxinz  
這篇文章主要為大家介紹了Go不支持[]T轉(zhuǎn)換為[]interface類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

在 Go 中,如果 interface{} 作為函數(shù)參數(shù)的話,是可以傳任意參數(shù)的,然后通過(guò)類型斷言來(lái)轉(zhuǎn)換。

舉個(gè)例子:

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,最終都能輸出正確結(jié)果。

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

比如下面這段代碼:

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

很遺憾,這段代碼是不能編譯通過(guò)的,如果想直接通過(guò) b := []interface{}(a) 的方式來(lái)轉(zhuǎn)換,還是會(huì)報(bào)錯(cuò):

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

正確的轉(zhuǎn)換方式需要這樣寫(xiě):

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

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

官方解釋

這個(gè)問(wèn)題在官方 Wiki 中是有回答的,我復(fù)制出來(lái)放在下面:

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.

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

  • []interface{} 類型并不是 interface,它是一個(gè)切片,只不過(guò)碰巧它的元素是 interface;
  • []interface{} 是有特殊內(nèi)存布局的,跟 interface 不一樣。

下面就來(lái)詳細(xì)說(shuō)說(shuō),是怎么個(gè)不一樣。

內(nèi)存布局

首先來(lái)看看 slice 在內(nèi)存中是如何存儲(chǔ)的。在源碼中,它是這樣定義的:

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

舉個(gè)例子,創(chuàng)建如下一個(gè)切片:

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

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

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

接下來(lái)再看看 []interface{} 在內(nèi)存中是什么樣的。

回答這個(gè)問(wèn)題之前先看一下 interface{} 的結(jié)構(gòu),Go 中的接口類型分成兩類:

  • iface 表示包含方法的接口;
  • eface 表示不包含方法的空接口。

源碼中的定義分別如下:

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

具體細(xì)節(jié)我們不去深究,但可以明確的是,每個(gè) interface{} 包含兩個(gè)指針, 會(huì)占據(jù)兩個(gè)「正方形」。第一個(gè)指針指向 itab 或者 _type;第二個(gè)指針指向?qū)嶋H的數(shù)據(jù)。

所以它在內(nèi)存中的布局如下圖所示:

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

程序運(yùn)行中的內(nèi)存布局

接下來(lái)?yè)Q一個(gè)更形象的方式,從程序?qū)嶋H運(yùn)行過(guò)程中,看看內(nèi)存的分布是怎么樣的?

看下面這樣一段代碼:

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 來(lái)進(jìn)行調(diào)試,可以點(diǎn)擊這里進(jìn)行安裝。

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)

接下來(lái)看看 slice 在內(nèi)存中都包含了哪些內(nèi)容:

(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 個(gè)字節(jié),也就是上文說(shuō)的一個(gè)「正方形」。第一行是指向數(shù)據(jù)的地址;第二行是 4,表示切片長(zhǎng)度;第三行也是 4,表示切片容量。

再來(lái)看看指向的數(shù)據(jù)到底是怎么存的:

(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

這就是一片連續(xù)的存儲(chǔ)空間,保存著實(shí)際數(shù)據(jù)。

接下來(lái)用同樣的方式,再來(lái)看看 iis 的內(nèi)存布局。

(dlv) p &amp;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 是一樣的,主要的不同是所指向的數(shù)據(jù):

(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

仔細(xì)觀察上面的數(shù)據(jù),偶數(shù)行內(nèi)容都是相同的,這個(gè)是 interface{}itab 地址。奇數(shù)行內(nèi)容是不同的,指向?qū)嶋H的數(shù)據(jù)。

打印地址內(nèi)容:

(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

很明顯,通過(guò)打印程序運(yùn)行中的狀態(tài),和我們的理論分析是一致的。

通用方法

通過(guò)以上分析,我們知道了不能轉(zhuǎn)換的原因,那有沒(méi)有一個(gè)通用方法呢?因?yàn)槲覍?shí)在是不想每次多寫(xiě)那幾行代碼。

也是有的,用反射 reflect,但是缺點(diǎn)也很明顯,效率會(huì)差一些,不建議使用。

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 支持的泛型,這里就不過(guò)多介紹了,大家有興趣的話可以繼續(xù)研究。

參考文章:

以上就是Go 不支持 []T 轉(zhuǎn)換為 []interface詳解的詳細(xì)內(nèi)容,更多關(guān)于Go不支持[]T轉(zhuǎn)換[]interface的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang 實(shí)現(xiàn)interface類型轉(zhuǎn)string類型

    Golang 實(shí)現(xiàn)interface類型轉(zhuǎn)string類型

    這篇文章主要介紹了Golang 實(shí)現(xiàn)interface類型轉(zhuǎn)string類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 淺析Golang中類型嵌入的簡(jiǎn)介與使用

    淺析Golang中類型嵌入的簡(jiǎn)介與使用

    類型嵌入指的就是在一個(gè)類型的定義中嵌入了其他類型,Go?語(yǔ)言支持兩種類型嵌入,分別是接口類型的類型嵌入和結(jié)構(gòu)體類型的類型嵌入,下面我們就來(lái)詳細(xì)一下類型嵌入的使用吧
    2023-11-11
  • 在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程

    在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程

    這篇文章主要介紹了在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程,由于國(guó)內(nèi)很多人對(duì)谷歌的盲目追捧,導(dǎo)致Go語(yǔ)言在國(guó)內(nèi)的人氣遠(yuǎn)超國(guó)外...需要的朋友可以參考下
    2015-06-06
  • 一文帶你搞懂Golang如何正確退出Goroutine

    一文帶你搞懂Golang如何正確退出Goroutine

    在Go語(yǔ)言中,Goroutine是一種輕量級(jí)線程,它的退出機(jī)制對(duì)于并發(fā)編程至關(guān)重要,下午就來(lái)介紹幾種Goroutine的退出機(jī)制,希望對(duì)大家有所幫助
    2023-06-06
  • Golang解析yaml文件的方法小結(jié)

    Golang解析yaml文件的方法小結(jié)

    Go 語(yǔ)言沒(méi)有內(nèi)置解析 yaml 文件的功能,實(shí)現(xiàn) yaml 的解析可以使用第三方庫(kù),下面我們就來(lái)看看如何使用opkg.in/yaml.v2 和 gopkg.in/yaml.v3實(shí)現(xiàn)解析yaml吧
    2024-11-11
  • 關(guān)于Go語(yǔ)言中特有的設(shè)計(jì)模式與實(shí)現(xiàn)方式講解

    關(guān)于Go語(yǔ)言中特有的設(shè)計(jì)模式與實(shí)現(xiàn)方式講解

    雖然Go語(yǔ)言沒(méi)有像其他語(yǔ)言那樣明確的設(shè)計(jì)模式,但在實(shí)踐中,開(kāi)發(fā)者們?nèi)匀话l(fā)現(xiàn)了一些在Go語(yǔ)言中特別適用的設(shè)計(jì)模式和實(shí)現(xiàn)方式,本文就來(lái)和大家一一進(jìn)行講解
    2023-05-05
  • 如何使用?Go?和?Excelize?構(gòu)建電子表格

    如何使用?Go?和?Excelize?構(gòu)建電子表格

    這篇文章主要介紹了如何使用Go和Excelize構(gòu)建電子表格,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • Go singleflight使用以及原理

    Go singleflight使用以及原理

    singleflight官方解釋其為:singleflight提供了一個(gè)重復(fù)的函數(shù)調(diào)用抑制機(jī)制。通俗的解釋其作用是,若有多個(gè)協(xié)程運(yùn)行某函數(shù)時(shí),只讓一個(gè)協(xié)程去處理,然后批量返回。非常適合來(lái)做并發(fā)控制。常見(jiàn)用于緩存穿透的情況
    2023-01-01
  • Go1.18新特性使用Generics泛型進(jìn)行流式處理

    Go1.18新特性使用Generics泛型進(jìn)行流式處理

    這篇文章主要為大家介紹了Go1.18新特性使用Generics泛型進(jìn)行流式處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 深入理解go?reflect反射慢的原因

    深入理解go?reflect反射慢的原因

    我們選擇?go?語(yǔ)言的一個(gè)重要原因是,它有非常高的性能。但是它反射的性能卻一直為人所詬病,本篇文章就來(lái)看看?go?反射的性能問(wèn)題,感興趣的可以了解一下
    2023-01-01

最新評(píng)論

西峡县| 上饶县| 云阳县| 和龙市| 平和县| 枣强县| 观塘区| 兴仁县| 平果县| 兴仁县| 原平市| 吴旗县| 黎川县| 集贤县| 墨脱县| 大庆市| 张家港市| 资兴市| 金华市| 桂阳县| 黎城县| 泰和县| 桂东县| 鹿邑县| 九龙坡区| 万全县| 馆陶县| 大姚县| 景泰县| 丹凤县| 江永县| 蓬莱市| 轮台县| 渭南市| 聊城市| 丰原市| 都江堰市| 贵溪市| 隆林| 郧西县| 民县|