你没看错,这里讲的就是 Go 中的泛型 。只不过还没有正式发布,是基于草案设计的,已经是实现了可运行的版本 。所以,泛型到来真的不远了!Go 中的泛型已经接近成为现实 。本文讲述的是泛型的最新设计,以及如何自己尝试泛型 。

文章插图
Generics in Go —— How They Work and How to Play With Them
【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/op 48 B/op 2 allocs/opBenchmarkGo2BenchmarkGo2-16 28406479 41.9 ns/op 24 B/op 2 allocs/op在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍 。合约(Contracts)上面的堆栈示例适用于任何类型 。但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码 。例如,你可能希望堆栈要求类型实现 String() 函数 。这就是 Contracts :
contract stringer(T) { T String() string}type Stack(type T stringer) []T// Now we can use the String method of T:func (s Stack(T)) String() string { ret := "" for _, v := range s { if ret != "" { ret += ", " } ret += v.String() } return ret}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 最常用的两种C++序列化方案的使用心得
- 翡翠戒指戴法是什么?
- 东南第一佳味天下之美的菜系是什么?
- Mysql 单表适合的最大数据量是多少?如何优化其性能?
- 高速公路收费标准是怎样的?
- 为什么福克斯跑高速稳?
- Java 内存泄露的理解与解决过程
- 双层玻璃都隔音吗?
- 自由女神像是哪个国家送给美国的礼物?
- 淘宝促销宝是干什么的 淘宝促销宝怎么使用
