
这是Go泛型系列文章。
其它Go泛型文章:
Go标准库中有一些单例的实现,比如log
包中默认的Logger
、net.DefaultResolver
, 这些对象提供了便利的方法,但是有的时候,我们需要做一些定制话的功能,需要更改这些对象,
甚至有的时候,我们需要更改标准库的特定方法,常规手段是不起作用的, 必须使用一些"骇客"的方式。
国庆北京连绵秋雨,整好我窝在家里,实现了一个原本想12月份实现的产品,在开发项目的过程中,也遇到了一些需要更改标准库默认行为的需求,所以在这方面做了一些探索,整理出这篇文章,以飨读者。
Go 1.17中你就可以使用泛型了,可以参考我3月份的文章:Go 泛型尝鲜, 编译的时候需要加-gcflags=-G=3
参数,而当前master分支,默认已经支持泛型,不需要加-G=3
参数了。
你可以通过下面的步骤尝试go最新分支:
|
|
编译代码的时候使用gotip
替换go
命令即可。
随着Go 1.17的发布,最近也涌现了很多的介绍Go泛型的文章,基本上都是简单介绍的文章。
最近Go泛型的变化是增加了两个操作符: ~
和|
:
~T
restricts to all types whose underlying type is T: 代表底层类型是T
T1 | T2 | ...
restricts to any of the listed elements: 代表或
,类型列表之一。这些不是我想介绍的内容,今天我肝一篇介绍Go泛型实现原理的文章,介绍Go泛型实现的方案。
对于一个函数func Echo[T any](t T){}
,Go编译器到底编译成了什么代码?
简单的说,当前Go泛型实现的方案和下图中的方案一样:
这是Russ Cox的第二篇Programming Language Memory Models。
如果你已经阅读了前一篇硬件内存模型,以及如果有Java内存模型或者C++内存模型的经验,本文还好理解,如果你没有相关经验,可能阅读起来比较费劲,建议先阅读一下相关的材料。论文有些词句比较难以理解,本人才学疏浅,有翻译不当之处欢迎批评指正。
正常情况下,我们的go.mod依赖库的版本都是符合语义化版本 2.0.0的版本格式,或者伪版本格式。在前面的文章我没有特别提到一点的事,Go使用服务端提交的日期和commit id生成的伪版本号是符合语义化版本号2.0.0的,因为语义化版本号中规定pre-release以连接号-加一连串以逗号分隔的标识符组成,标识符以字母数字和连接号组成,所以你看到-yyyyMMddhhmmss-comitid
包含两个连接号,这是正常的。
go要求依赖库要么不包含go.mod,要么依赖库中的go.mod定义的依赖库版本必须以语义化版本 2.0.0格式(或伪版本号)标志(其实更严格,除了+incompatible
不能加meta字段),因为这样我们你能够明确标识某个依赖库确切的版本,这样的版本号被称之为canonical version。
其实main module还可以定义non-canonical version,通过go get或者go mod tidy更新go.mod的时候,命令会尝试更新go.mod,尝试把non-canonical version转变为canonical version版本。
但是,到底有哪些non-canonical version呢?我还没看到官方文章介绍,本文尝试整理这些non-canonical version。