當前位置:編程學習大全網 - 編程語言 - 如何看待go語言泛型的最新設計?

如何看待go語言泛型的最新設計?

Go 由於不支持泛型而臭名昭著,但最近,泛型已接近成為現實。Go 團隊實施了壹個看起來比較穩定的設計草案,並且正以源到源翻譯器原型的形式獲得關註。本文講述的是泛型的最新設計,以及如何自己嘗試泛型。

例子

FIFO Stack

假設妳要創建壹個先進先出堆棧。沒有泛型,妳可能會這樣實現:

type?Stack?[]interface{}func?(s?Stack)?Peek()?interface{}?{

return?s[len(s)-1]

}

func?(s?*Stack)?Pop()?{

*s?=?(*s)[:

len(*s)-1]

}

func?(s?*Stack)?Push(value?interface{})?{

*s?=?

append(*s,?value)

}

但是,這裏存在壹個問題:每當妳 Peek 項時,都必須使用類型斷言將其從 interface{} 轉換為妳需要的類型。如果妳的堆棧是 *MyObject 的堆棧,則意味著很多 s.Peek().(*MyObject)這樣的代碼。這不僅讓人眼花繚亂,而且還可能引發錯誤。比如忘記 * 怎麽辦?或者如果您輸入錯誤的類型怎麽辦?s.Push(MyObject{})` 可以順利編譯,而且妳可能不會發現到自己的錯誤,直到它影響到妳的整個服務為止。

通常,使用 interface{} 是相對危險的。使用更多受限制的類型總是更安全,因為可以在編譯時而不是運行時發現問題。

泛型通過允許類型具有類型參數來解決此問題:

type?Stack(type?T)?[]Tfunc?(s?Stack(T))?Peek()?T?{

return?s[len(s)-1]

}

func?(s?*Stack(T))?Pop()?{

*s?=?(*s)[:

len(*s)-1]

}

func?(s?*Stack(T))?Push(value?T)?{

*s?=?

append(*s,?value)

}

這會向 Stack 添加壹個類型參數,從而完全不需要 interface{}。現在,當妳使用 Peek() 時,返回的值已經是原始類型,並且沒有機會返回錯誤的值類型。這種方式更安全,更容易使用。(譯註:就是看起來更醜陋,^-^)

此外,泛型代碼通常更易於編譯器優化,從而獲得更好的性能(以二進制大小為代價)。如果我們對上面的非泛型代碼和泛型代碼進行基準測試,我們可以看到區別:

type?MyObject?struct?{

X?

int

}

var?sink?MyObjectfunc?BenchmarkGo1(b?*testing.B)?{

for?i?:=?0;?i?<?b.N;?i++?{

var?s?Stack

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink?=?s.Peek().(MyObject)

}

}

func?BenchmarkGo2(b?*testing.B)?{

for?i?:=?0;?i?<?b.N;?i++?{

var?s?Stack(MyObject)

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink?=?s.Peek()

}

}

結果:

BenchmarkGo1BenchmarkGo1-16?12837528?87.0?ns/op48?B/op2?allocs/opBenchmarkGo2BenchmarkGo2-16?28406479?41.9?ns/op24?B/op2?allocs/op

在這種情況下,我們分配更少的內存,同時泛型的速度是非泛型的兩倍。

合約(Contracts)

上面的堆棧示例適用於任何類型。但是,在許多情況下,妳需要編寫僅適用於具有某些特征的類型的代碼。例如,妳可能希望堆棧要求類型實現 String() 函數

  • 上一篇:高中信息技術html簡單代碼有哪些
  • 下一篇:計算機調研報告怎麽寫?
  • copyright 2024編程學習大全網