网站首页 > 文章中心 > 其它

go语言泛型怎么处理

作者:小编 更新时间:2023-10-20 10:16:15 浏览量:141人看过

尝试用golang 1.18泛型实现orm

这几天golang社区对泛型的讨论非常多的,一片热火朝天的景象.对我们广大gopher来说总归是好事.

泛型很有可能会颠覆我们之前的很多设计,带着这种疑问和冲动,我准备尝试用golang泛型实现几个orm的常见功能.

本文并没完全实现通用的orm,只是探讨其实现的一种方式提供各位读者做借鉴.

虽然golang有了泛型,但是目前在标准库sql底层还没有改造,目前还有很多地方需要用到reflect.

go语言泛型怎么处理-图1

调用方式

这个部分跟传统的orm使用上没有太大区别,没办法不使用反射的情况下,泛型的方式可能变得有点繁琐.

和创建table类似,写入数据好像比没有之前的orm有优势.

读取数据是非常高频的操作,所以我们稍作封装.

稍微比原先的orm方式有了多一点想象空间,比如 在[T any]做更明确的约束,比如要求实现Filter定制方法.

鉴于本人能力还认证有限,目前还没有发现泛型对orm剧烈的改进和突破的可能.未来如果go对底层sql做出改动,或者实现诸如Rust那种Enum方式,可能会带来更多的惊喜.

go语言泛型怎么处理-图2

如何看待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?{

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

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

这会向 Stack 添加一个类型参数,从而完全不需要 interface{}.现在,当你使用 Peek() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型.这种方式更安全,更容易使用.(译注:就是看起来更丑陋,^-^)

此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价).如果我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:

go语言泛型怎么处理-图3

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.Pop()

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

var?s?Stack(MyObject)

sink?=?s.Peek()

结果:

在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍.

合约(Contracts)

上面的堆栈示例适用于任何类型.但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码.例如,你可能希望堆栈要求类型实现 String() 函数

GO语言(十五):泛型入门(下)-

在本节中,您将添加通用函数调用的修改版本,进行小的更改以简化调用代码.您将删除在这种情况下不需要的类型参数.

当 Go 编译器可以推断您要使用的类型时,您可以在调用代码中省略类型参数.编译器从函数参数的类型推断类型参数.

请注意,这并不总是可能的.例如,如果您需要调用没有参数的泛型函数,则需要在函数调用中包含类型参数.

在 main.go 中,在您已有的代码下方,粘贴以下代码.

在此代码中:

(1)调用泛型函数,省略类型参数.

从包含 main.go 的目录中的命令行,运行代码.

此时此刻呢,您将通过将整数和浮点数的并集捕获到您可以重用的类型约束(例如从其他代码中)来进一步简化函数.

正如您将在本节中看到的,约束接口也可以引用特定类型.

①.、编写代码

b.在您已有的函数下方,粘贴以下通用 SumNumbers函数.

c.在 main.go 中,在您已有的代码下方,粘贴以下代码.

(1)调用SumNumbers打印每个map的总和.

与上一节一样,在调用泛型函数时省略了类型参数(方括号中的类型名称).Go 编译器可以从其他参数推断类型参数.

做得很好!您刚刚学习了 Go 中的泛型.

大家知道为什么golang不支持泛型

Golang团队认为在类型系统和运行时的复杂性花费太大,还没找到可以和这个复杂性相抵的良好设计.内置的map和slice其实都有泛型的味道,加上可以用interface{}来构造容器,可以达到泛型的效果.所以目前为止还没有直接的支持泛型.

以上就是土嘎嘎小编为大家整理的go语言泛型怎么处理相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!

版权声明:倡导尊重与保护知识产权。未经许可,任何人不得复制、转载、或以其他方式使用本站《原创》内容,违者将追究其法律责任。本站文章内容,部分图片来源于网络,如有侵权,请联系我们修改或者删除处理。

编辑推荐

热门文章