对于程序员来说,编写的代码依赖标准库是“天经地义”的事情。标准库在程序员眼中就是高质量的代名词,也是最值得信赖的非自己所写的代码,当然更是代码包依赖关系链条上的最后一环,即所有直接或间接依赖的第三方module最终都会依赖标准库。
前两天组内学习rust[1]的小伙伴说rust的标准库还依赖第三方库(注:我对rust了解不深,尚未证实),这引发了我的一个疑问: Go标准库是否依赖其他modules呢?在这一短篇中,我就来探究一下
注:本文使用的Go版本为Go 1.19[2]。
众所周知,Go于1.11版本引入go modules[3],如今Go module已经完全替代掉原先的gopath构建模式,成为了Go源码的标准构建模式。
相应的,Go标准库也在Go 1.13版本[4]中采用了Go module构建,加入了go.mod文件,第一版标准库的go.mod文件内容如下:
module std
go 1.12require (
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirect
golang.org/x/text v0.3.2 // indirect
)
我们看到Go标准库的module path为std。不过就像开篇说的那样,很多gopher认为标准库应该是依赖链的末端,但从go.mod文件的内容来看,Go标准库也有自己的依赖。
我们再来看看Go 1.19版本[5]中go.mod的内容:
module std
go 1.19require (
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8
golang.org/x/net v0.0.0-20220517181318-183a9ca12b87
)
require (
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 // indirect
golang.org/x/text v0.3.8-0.20220509174342-b4bca84b0361 // indirect
)
我们看到:和Go 1.13版本相比,Go标准库的go.mod将直接依赖和间接依赖(也叫传递依赖)分开放在不同的require block中,这是因为Go 1.17版本增加的module依赖图修剪特性[6]。
但从Go标准库依赖的modules来看,和Go 1.13相比,Go标准库依赖的modules并没有变化。
Go标准库依赖的是什么modules呢?我们看到其依赖的module都在golang.org/x这个路径下,这是Go核心团队自己维护的非标准库module的Canonical import paths[7]的前缀路径。golang.org/x这个前缀路径下的包有不少,如下图所示:
其中,主要的可以被import的功能module包括:
注:exp下面的包尽量不用,或务必谨慎使用,这里实验性包居多,API接口和具体实现变化可能性很大。还有一些是废弃不再维护的。
那Go标准库为什么会直接依赖crypto和net这两个modules呢?
我的理解是网络与密码学是两个变化较快的领域,同时也是两个十分重要的领域,尤其是在如今对安全十分重视的云原生时代。一些新的密码学算法、网络技术规范(RFC)在不断的出现并持续演进,这些技术在未成熟前尚不适合放入标准库,那么在标准库之外由Go核心团队维护一个“与时俱进”的库就十分必要。等成熟后,在标准库中设计并提供稳定接口并引用golang.org/x/abc下的实现就可以很快实现对某成熟网络技术或密码学技术的稳定支持,当年Go 1.6版本对http/2的支持就是这么做的[8]。
那么Go标准库都依赖了哪些具体的包了呢?我们可以看一下$GOROOT/src/vendor下面的modules.txt:
# golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8
## explicit; go 1.17
golang.org/x/crypto/chacha20
golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/cryptobyte
golang.org/x/crypto/cryptobyte/asn1
golang.org/x/crypto/curve25519
golang.org/x/crypto/curve25519/internal/field
golang.org/x/crypto/hkdf
golang.org/x/crypto/internal/poly1305
golang.org/x/crypto/internal/subtle
# golang.org/x/net v0.0.0-20220517181318-183a9ca12b87
## explicit; go 1.17
golang.org/x/net/dns/dnsmessage
golang.org/x/net/http/httpguts
golang.org/x/net/http/httpproxy
golang.org/x/net/http2/hpack
golang.org/x/net/idna
golang.org/x/net/lif
golang.org/x/net/nettest
golang.org/x/net/route
# golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098
## explicit; go 1.17
golang.org/x/sys/cpu
# golang.org/x/text v0.3.8-0.20220509174342-b4bca84b0361
## explicit; go 1.17
golang.org/x/text/secure/bidirule
golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
modules.txt是go mod vendor命令生成的,也是项目依赖包的完全列表,包括间接依赖的包。
我们可以通过go mod why命令查询为什么标准库要依赖这些module以及package,以golang.org/x/crypto这个module为例:
$go mod why -m golang.org/x/crypto
# golang.org/x/crypto
crypto/tls
golang.org/x/crypto/chacha20poly1305
我们看到是crypto/tls包依赖了golang.org/x/crypto这个module,但why只会输出标准库中依赖x/crypto module的一个包而已,并非全部。同理我们也可以查看modules.txt某个具体的包为何要被依赖,以golang.org/x/net/dns/dnsmessage为例:
$go mod why golang.org/x/net/dns/dnsmessage
# golang.org/x/net/dns/dnsmessage
net
golang.org/x/net/dns/dnsmessage
我们看到net包依赖了dnsmessage这个包。
综上,我们知道了Go标准库也是会依赖的,但其依赖的module被严格限制在Go核心团队自己维护的golang.org/x下面的少数module,因此我们依然可以完全信任Go标准库,相信后续Go标准库也会一直保证实现的高质量。
学习rust: https://tonybai.com/2021/03/15/rust-vs-go-why-they-are-better-together
[2]Go 1.19: https://tonybai.com/2022/08/22/some-changes-in-go-1-19
[3]Go于1.11版本引入go modules: https://tonybai.com/2018/11/19/some-changes-in-go-1-11/
[4]Go 1.13版本: https://tonybai.com/2019/10/27/some-changes-in-go-1-13/
[5]Go 1.19版本: https://tonybai.com/2022/08/22/some-changes-in-go-1-19
[6]Go 1.17版本增加的module依赖图修剪特性: https://tonybai.com/2021/08/19/go-module-changes-in-go-1-17
[7]Canonical import paths: https://tonybai.com/2014/11/04/some-changes-in-go-1-4
[8]Go 1.6版本对http/2的支持就是这么做的: https://tonybai.com/2016/02/21/some-changes-in-go-1-6
[9]“Gopher部落”知识星球: https://wx.zsxq.com/dweb2/index/group/51284458844544
推荐阅读