詳解Go語言中泛型的實現(xiàn)原理與使用_第1頁
詳解Go語言中泛型的實現(xiàn)原理與使用_第2頁
詳解Go語言中泛型的實現(xiàn)原理與使用_第3頁
詳解Go語言中泛型的實現(xiàn)原理與使用_第4頁
詳解Go語言中泛型的實現(xiàn)原理與使用_第5頁
已閱讀5頁,還剩3頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

第詳解Go語言中泛型的實現(xiàn)原理與使用目錄前言問題解決方法類型約束重獲類型安全泛型使用場景性能虛擬方法表單態(tài)化Go的實現(xiàn)結論

前言

原文:AgentleintroductiontogenericsinGobyDominikBraun

萬俊峰Kevin:我看了覺得文章非常簡單易懂,就征求了作者同意,翻譯出來給大家分享一下。

本文是對泛型的基本思想及其在Go中的實現(xiàn)的一個比較容易理解的介紹,同時也是對圍繞泛型的各種性能討論的簡單總結。首先,我們來看看泛型所解決的核心問題。

問題

假設我們想實現(xiàn)一個簡單的tree數(shù)據(jù)結構。每個節(jié)點持有一個值。在Go1.18之前,實現(xiàn)這種結構的典型方法如下。

type

Node

struct

{

value

interface{}

這在大多數(shù)情況下都很好用,但它也有一些缺點。

首先,interface{}可以是任何東西。如果我們想限制value可能持有的類型,例如整數(shù)和浮點數(shù),我們只能在運行時檢查這個限制。

func

(n

Node)

IsValid()

bool

{

switch

n.value.(type)

{

case

int,

float32,

float64:

return

true

default:

return

false

}

這樣并不可能在編譯時限制類型,像上面這樣的類型判斷在許多Go庫中都是很常見的做法。這里有go-zero項目中的例子。

第二,對Node中的值進行處理是非常繁瑣和容易出錯的。對值做任何事情都會涉及到某種類型的斷言,即使你可以安全地假設值持有一個int值。

number,

ok

:=

node.value.(int)

if

!ok

{

//

...

double

:=

number

*

2

這些只是使用interface{}的一些不便之處,它沒有提供類型安全,并有可能導致難以恢復的運行時錯誤。

解決方法

我們不打算接受任意數(shù)據(jù)類型或具體類型,而是定義一個叫做T的占位符類型作為值的類型。請注意,這段代碼還不會通過編譯。

type

Node[T]

struct

{

value

T

首先需要聲明泛型類型T,這是在結構或函數(shù)名稱后面方括號里面使用的。

T可以是任何類型,只有在實例化一個具有明確類型的Node時,T才會被推導為該類型。

n

:=

Node[int]{

value:

5,

泛型Node被實例化為Node[int](整數(shù)節(jié)點),所以T是一個int。

類型約束

上面的實現(xiàn)里,T的聲明缺少一個必要的信息:類型約束。

類型約束用于進一步限制可以作為T的可能類型。Go本身提供了一些預定義的類型約束,但也可以使用自定義的類型約束。

type

Node[T

any]

struct

{

value

T

任意類型(any)約束允許T實際上是任何類型。如果節(jié)點值需要進行比較,有一個comparable類型約束,滿足這個預定義約束的類型可以使用==進行比較。

type

Node[T

comparable]

struct

{

value

T

任何類型都可以作為一個類型約束。Go1.18引入了一種新的interface語法,可以嵌入其他數(shù)據(jù)類型。

type

Numeric

interface

{

int

|

float32

|

float64

這意味著一個接口不僅可以定義一組方法,還可以定義一組類型。使用Numeric接口作為類型約束,意味著值可以是整數(shù)或浮點數(shù)。

type

Node[T

Numeric]

struct

{

value

T

重獲類型安全

相對于使用interface{},泛型類型參數(shù)的巨大優(yōu)勢在于,T的最終類型在編譯時就會被推導出來。為T定義一個類型約束,完全消除了運行時檢查。如果用作T的類型不滿足類型約束,代碼就不會編譯通過。

在編寫泛型代碼時,你可以像已經(jīng)知道T的最終類型一樣寫代碼。

func

(n

Node[T])

Value()

T

{

return

n.value

上面的函數(shù)返回n.Value,它的類型是T。因此,返回值是T,如果T是一個整數(shù),那么返回類型就已知是int。因此,返回值可以直接作為一個整數(shù)使用,不需要任何類型斷言。

n

:=

Node[int]{

value:

5,

double

:=

n.Value()

*

2

在編譯時恢復類型安全使Go代碼更可靠,更不容易出錯。

泛型使用場景

在IanLanceTaylor的WhenToUseGenerics中列出了泛型的典型使用場景,歸結為三種主要情況:

使用內(nèi)置的容器類型,如slices、maps和channels實現(xiàn)通用的數(shù)據(jù)結構,如linkedlist或tree編寫一個函數(shù),其實現(xiàn)對許多類型來說都是一樣的,比如一個排序函數(shù)

一般來說,當你不想對你所操作的值的內(nèi)容做出假設時,可以考慮使用泛型。我們例子中的Node并不太關心它持有的值。

當不同的類型有不同的實現(xiàn)時,泛型就不是一個好的選擇。另外,不要把Read(rio.Reader)這樣的接口函數(shù)簽名改為Read[Tio.Reader](rT)這樣的通用簽名。

性能

要了解泛型的性能及其在Go中的實現(xiàn),首先需要了解一般情況下實現(xiàn)泛型的兩種最常見方式。

這是對各種性能的深入研究和圍繞它們進行的討論的簡要介紹。你大概率不太需要關心Go中泛型的性能。

虛擬方法表

在編譯器中實現(xiàn)泛型的一種方法是使用VirtualMethodTable。泛型函數(shù)被修改成只接受指針作為參數(shù)的方式。然后,這些值被分配到堆上,這些值的指針被傳遞給泛型函數(shù)。這樣做是因為指針看起來總是一樣的,不管它指向的是什么類型。

如果這些值是對象,而泛型函數(shù)需要調(diào)用這些對象的方法,它就不能再這樣做了。該函數(shù)只有一個指向?qū)ο蟮闹羔槪恢浪鼈兊姆椒ㄔ谀睦?。因此,它需要一個可以查詢方法的內(nèi)存地址的表格:VirtualMethodTable。這種所謂的動態(tài)調(diào)度已經(jīng)被Go和Java等語言中的接口所使用。

VirtualMethodTable不僅可以用來實現(xiàn)泛型,還可以用來實現(xiàn)其他類型的多態(tài)性。然而,推導這些指針和調(diào)用虛擬函數(shù)要比直接調(diào)用函數(shù)慢,而且使用VirtualMethodTable會阻止編譯器進行優(yōu)化。

單態(tài)化

一個更簡單的方法是單態(tài)化(Monomorphization),編譯器為每個被調(diào)用的數(shù)據(jù)類型生成一個泛型函數(shù)的副本。

func

max[T

Numeric](a,

b

T)

T

{

//

...

larger

:=

max(3,

5)

由于上面顯示的max函數(shù)是用兩個整數(shù)調(diào)用的,編譯器在對代碼進行單態(tài)化時將為int生成一個max的副本。

func

maxInt(a,

b

int)

int

{

//

...

larger

:=

maxInt(3,

5)

最大的優(yōu)勢是,Monomorphization帶來的運行時性能明顯好于使用VirtualMethodTable。直接方法調(diào)用不僅更有效率,而且還能適用整個編譯器的優(yōu)化鏈。不過,這樣做的代價是編譯時長,為所有相關類型生成泛型函數(shù)的副本是非常耗時的。

Go的實現(xiàn)

這兩種方法中哪一種最適合Go?快速編譯很重要,但運行時性能也很重要。為了滿足這些要求,Go團隊決定在實現(xiàn)泛型時混合兩種方法。

Go使用Monomorphization,但試圖減少需要生成的函數(shù)副本的數(shù)量。它不是為每個類型創(chuàng)建一個副本,而是為內(nèi)存中的每個布局生成一個副本:int、float64、Node和其他所謂的值類型在內(nèi)存中看起來都不一樣,因此泛型函數(shù)將為所有這些類型復制副本。

與值類型相反,指針和接口在內(nèi)存中總是有相同的布局。編譯器將為指針和接口的調(diào)用生成一個泛型函數(shù)的副本。就像VirtualMethodTable一樣,泛型函數(shù)接收指針,因此需要一個表來動態(tài)地查找方法地址。在Go實現(xiàn)中的字典與虛擬方法表的性能特點相同。

結論

這種混合方法的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論