Golang v1.18给广大Gopher带来了期盼已久的泛型功能。但是很多人可能还只是观望测试阶段实际开发中可能还没有真正采用。本文我们以Brev.dev的泛型迁移经验总结来学习一下,为将来正式采用做好功课。

Brev.dev的泛型上线主要是用于共享集合库,从典型的函数集合库中导入函数,如 Haskell、Scala 或动态语言中的类似函数库,如lo-dash、underscore和JS的rambda库以及ruby的raskell库。

虽然到目前为止Brev.dev的泛型应用在golang 1.18 中运行良好,只是外网生态圈的一些工具,特别golangci-lint相关的工具,会有内存泄漏和panic崩溃的问题。

升级到1.18

安装或升级到golang 1.18很简单。需要做的仅仅是更新二进制文件并修改go.mod文件中的版本号。

比如Mac下

升级go二进制文件后,将能够编译和构建go 1.18 代码。

注意同步更新IDE 扩展、插件和其他开发工具。具体来说,对于VScode Go 扩展,请确保打开命令面板 (Cmd+Shift+P) 并运行Go: Install/Update Tools。

对于golangci-lint,需要确保go.mod中的版本为1.45或更高版本。最后,还要和同一项目的团队小伙伴们沟通好同步升级环境。

遇到的问题

开发工具链中仍有一些问题需要解决。此外,升级时必须小心——在此次迁移中发现如果不能完全升级linter等工具则可能会导致内存泄漏。项目中有一个单独的go.mod用于开发工具,例如golangci-lint、goreleaser和gofumpt。在golangci-lint的情况下,如果不将golangci-lint从v1.42升级到v1.45时出现莫名其妙的内存泄漏。

该内存泄露的bug可以用以下方法重现:

运行make lint就能重现该问题,该问题看起来像是一个使用千兆字节内存的进程,直到该进程被杀死,它消耗了所有可用内存。

在将golangci-lint升级到与1.18兼容的v1.45 后,linting工具不再泄漏内存,但是对泛型代码仍然会崩溃。

发生崩溃的原因是由于当前非所有linter都支持go 新泛型语法,并且而项目中.golangci.yml配置文件中恰好配置了几个。请注意,所有非泛型代码都可以正常工作,特别是泛型语法会破坏很多linter。目前发现不支持的泛型语法有:

可以克隆brevdev的代码重现该问题:

问题解决

在解决这些问题时,考虑到两种方法:注释掉受影响的的linters,并不在任何代码中使用它们,或者隔离泛型语法代码并禁用linting。

最后决定提取所有通用代码以分离包,并禁用这些文件的linter。可以使用此处看到的文件顶部的//go:build !codeanalysis注释禁用对特定文件的禁用linting。然后在每次导入泛型包时,禁用“typecheck”linter(如果此代码键入不正确,编译器仍然会失败)。

虽然这不是一个完美的解决方案,但随着go 泛型生态系统会的更新,这个问题很快就可以解决,期待golangci-lint的更新迭代。

总结

brevdev 初次使用Go 1.18生态系统的体验并算顺畅的,但泛型的体验结果却非常积极。如果项目可以从泛型中受益,建议大家大胆尝试并总结经验反馈出来。