成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么 Go 不支持 []T 轉(zhuǎn)換為 []interface

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

在 Go 中,如果 interface{} 作為函數(shù)參數(shù)的話,是可以傳任意參數(shù)的,然后通過類型斷言來轉(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è)疑問了,拿出我舉一反三的能力。是否可以將 []T? 轉(zhuǎn)換為 []interface 呢?

比如下面這段代碼:

func foo([]interface{}) { /* do something */ }

func main() {
var a []string = []string{"hello", "world"}
foo(a)
}

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

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

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

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

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

官方解釋

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

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?,它是一個(gè)切片,只不過碰巧它的元素是interface;
  2. []interface{}? 是有特殊內(nèi)存布局的,跟interface 不一樣。

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

內(nè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。

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

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

  1. iface 表示包含方法的接口;
  2. 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)存布局

接下來?yè)Q一個(gè)更形象的方式,從程序?qū)嶋H運(yùn)行過程中,看看內(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 來進(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)

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

再來看看指向的數(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ù)。

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

(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 是一樣的,主要的不同是所指向的數(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

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

通用方法

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

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

以上就是本文的全部?jī)?nèi)容,如果覺得還不錯(cuò)的話歡迎點(diǎn)贊,轉(zhuǎn)發(fā)和關(guān)注,感謝支持。

參考文章:

  • https://stackoverflow.com/questions/12753805/type-converting-slices-of-interfaces
  • https://github.com/golang/go/wiki/InterfaceSlice
  • https://eli.thegreenplace.net/2021/go-internals-invariance-and-memory-layout-of-slices/
責(zé)任編輯:武曉燕 來源: AlwaysBeta
相關(guān)推薦

2021-10-27 07:15:36

Go 循環(huán)引用

2021-12-09 10:51:47

Go繼承

2021-12-15 07:49:22

Go語言設(shè)計(jì)

2024-01-01 08:10:40

Go語言map

2024-01-05 08:45:35

Go語言map

2024-05-28 08:55:52

2021-11-08 11:02:01

Go函數(shù)重載

2023-02-26 23:36:08

PHPGo函數(shù)

2024-03-08 08:51:59

Gomain函數(shù)

2020-07-22 08:01:41

Python開發(fā)運(yùn)算符

2020-10-09 06:48:19

Pythonswitch語句

2024-03-12 09:13:28

Go語言main

2021-02-01 13:53:53

StringlongJava

2009-03-12 08:42:38

AndroidWMMTK

2021-06-11 00:03:31

鴻蒙智能手機(jī)

2021-08-02 09:31:20

Python工具代碼

2025-06-26 02:22:00

GoProtobuf標(biāo)簽

2021-07-13 08:09:34

微博推特評(píng)論

2023-04-03 11:21:29

PythonGoRust

2009-03-11 17:32:22

聯(lián)發(fā)科WMAndroid
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 成人一区二区三区 | 自拍视频一区二区三区 | 欧美2区| 亚洲精品888 | 亚洲人成人一区二区在线观看 | 欧美日韩亚洲在线 | 成人性生交大片免费看中文带字幕 | 久久久久国产一级毛片高清网站 | 欧美日一区二区 | 成人黄色在线观看 | 日韩免费视频一区二区 | 国产精品一区二区三区久久 | www.国产.com| 国产超碰人人爽人人做人人爱 | 波多野结衣先锋影音 | 午夜免费观看体验区 | 欧美5区| 麻豆亚洲 | 99热精品久久 | 91久久精品一区二区二区 | 成人羞羞国产免费视频 | 成人在线不卡 | www.啪啪.com | 国产日韩精品一区二区 | 国产色婷婷精品综合在线手机播放 | 成人免费在线电影 | 视频一区在线观看 | 99久久精品免费看国产四区 | 欧美美女爱爱视频 | 欧美视频一区二区三区 | 久久国产成人 | 一本一道久久a久久精品蜜桃 | 亚洲精品国产偷自在线观看 | 91精品国产综合久久婷婷香蕉 | 亚洲精品一区二区三区中文字幕 | 嫩呦国产一区二区三区av | 狠狠爱免费视频 | 一区二区在线视频 | 精品视频一区二区在线观看 | 成人一级黄色毛片 | 久久最新精品 |