Go Interesting

这里记录一些golang使用中的小细节,每次都像cgo那样单独写一篇有点太水了。

go:build

https://stackoverflow.com/questions/68360688/whats-the-difference-between-gobuild-and-build-directives

起因是用vscode上编辑boltdb的代码,每次ctrl+s保存之后就会在// +build xxx, xxx前面加一句语义相同的//go:build blahblah

//go:build 是 Go 1.17 中引入的新条件编译指令。

它旨在替换// +这个build tag,因为新语法带来了一些关键改进:

与其他现有 Go 指令和编译指示的一致性,
例如//go:generate支持标准布尔表达式,
例如//go:build foo && bar,而旧的 // +build 注释的语法不太直观。例如 AND 用逗号表示 // +build foo, bar 和 OR 用空格 // +build foo bar
go fmt 支持自动修复源文件中指令的错误位置,从而避免在指令和包语句之间不留空行的常见错误。
这两个构建指令将在几个 Go 版本中共存,以确保平滑过渡,如相关提案文件中所述(在 N = 17 以下,重点是我的):

Go 1.N 将开始转换。在 Go 1.N 中:

构建将开始更喜欢 //go: 用于文件选择的构建行。如果没有 // go: build 在一个文件中,那么任何 // + 构建行仍然适用。

如果Go文件包含//go: build 而没有 // +build,构建将不再失败。

如果Go或程序集文件包含 // go: build 在文件中太过靠后,则构建将失败。 Gofmt 会将错位的 //go: build// +build 行移动到文件中的正确位置。

Gofmt 将使用与其他 Go 布尔表达式(所有 && 和 || 运算符周围的空格)相同的规则来格式化//go:build lines 中的表达式。

如果文件只包含// +build行,gofmt 将在它们上方添加一个等效的//go:build行。

如果文件同时包含 //go:build// +build 行,gofmt 将考虑 //go:build 的真实来源并更新 // +build 行以匹配,从而保持与早期版本的 Go 的兼容性。 Gofmt 也会拒绝 //go:build 被认为太复杂而无法转换为 // +build 格式的行,尽管这种情况很少见。 (注意此项目符号开头的“If”。Gofmt 不会将 // +build行添加到只有 //go:build 的文件中。)

go vet中的构建标签检查将添加对//go:build约束的支持。当 Go 源文件包含 // go:build// +build 具有不同含义时,它将失败。如果检查失败,可以运行 gofmt -w。

当 Go 源文件包含//go:build, 但不包含//+build, 且它的包含模块有一个go line列出 Go 1.N 之前的版本时,构建标签检查也会失败。如果检查失败,可以添加任何// +build行,然后运行 ​​gofmt -w,它将用正确的行替换它。或者可以将 go.mod go 版本升级到 Go 1.N。