Go 1.16 发布说明

Go 1.16 简介

最新的 Go 版本 1.16 在 Go 1.15 之后六个月发布。其大部分更改都在工具链、运行时和库的实现中。一如既往,此版本保持了 Go 1 的兼容性承诺。我们预计几乎所有 Go 程序都将像以前一样继续编译和运行。

语言变化

语言没有变化。

移植

Darwin 和 iOS

Go 1.16 添加了对 macOS 上 64 位 ARM 架构(也称为 Apple Silicon)的支持,使用 GOOS=darwin, GOARCH=arm64。与 darwin/amd64 移植一样,darwin/arm64 移植支持 cgo、内部和外部链接、c-archivec-sharedpie 构建模式以及竞态检测器。

之前是 darwin/arm64 的 iOS 移植已重命名为 ios/arm64GOOS=ios 意味着 darwin 构建标签,就像 GOOS=android 意味着 linux 构建标签一样。此更改对于使用 gomobile 构建 iOS 应用程序的任何人来说都应该是透明的。

引入 GOOS=ios 意味着像 x_ios.go 这样的文件名现在将仅为 GOOS=ios 构建;有关详细信息,请参阅 go help buildconstraint。使用这种形式文件名的现有包将不得不重命名这些文件。

Go 1.16 添加了 ios/amd64 移植,它针对在基于 AMD64 的 macOS 上运行的 iOS 模拟器。以前,这通过 darwin/amd64 并设置 ios 构建标签得到非官方支持。另请参阅 misc/ios/README,了解有关如何为 iOS 和 iOS 模拟器构建程序的详细信息。

Go 1.16 是最后一个在 macOS 10.12 Sierra 上运行的版本。Go 1.17 将需要 macOS 10.13 High Sierra 或更高版本。

NetBSD

Go 现在支持 NetBSD 上的 64 位 ARM 架构(netbsd/arm64 移植)。

OpenBSD

Go 现在支持 OpenBSD 上的 MIPS64 架构(openbsd/mips64 移植)。此移植尚不支持 cgo。

在 OpenBSD 上的 64 位 x86 和 64 位 ARM 架构(openbsd/amd64openbsd/arm64 移植)上,系统调用现在通过 libc 进行,而不是直接使用 SYSCALL/SVC 指令。这确保了与未来 OpenBSD 版本的向前兼容性。特别是,OpenBSD 6.9 及更高版本将要求非静态 Go 二进制文件的系统调用通过 libc 进行。

386

如 Go 1.15 发布说明中宣布,Go 1.16 放弃了对 x87 模式编译(GO386=387)的支持。现在可以使用软浮点模式(GO386=softfloat)支持非 SSE2 处理器。在非 SSE2 处理器上运行的用户应将 GO386=387 替换为 GO386=softfloat

RISC-V

linux/riscv64 移植现在支持 cgo 和 -buildmode=pie。此版本还包括 RISC-V 的性能优化和代码生成改进。

工具

Go 命令

模块

模块感知模式默认启用,无论当前工作目录或父目录中是否存在 go.mod 文件。更准确地说,GO111MODULE 环境变量现在默认为 on。要切换到以前的行为,请将 GO111MODULE 设置为 auto

默认情况下,go buildgo test 等构建命令不再修改 go.modgo.sum。相反,如果需要添加或更新模块要求或校验和,它们会报告错误(就像使用了 -mod=readonly 标志一样)。模块要求和总和可以通过 go mod tidygo get 进行调整。

go install 现在接受带版本后缀的参数(例如,go install example.com/cmd@v1.0.0)。这使得 go install 在模块感知模式下构建和安装包,如果当前目录或任何父目录中有 go.mod 文件,则忽略它。这对于安装可执行文件而不影响主模块的依赖项很有用。

go install,无论是否带版本后缀(如上所述),现在是模块模式下构建和安装包的推荐方式。go get 应该与 -d 标志一起使用,以调整当前模块的依赖项而不构建包,并且使用 go get 构建和安装包已被弃用。在未来的版本中,-d 标志将始终启用。

retract 指令现在可以在 go.mod 文件中使用,以指示模块的某些已发布版本不应被其他模块使用。模块作者可以在发现严重问题或无意中发布版本后撤回版本。

go mod vendorgo mod tidy 子命令现在接受 -e 标志,它指示它们在解决缺失包时遇到错误时继续进行。

go 命令现在忽略主模块中被 exclude 指令排除的模块版本的要求。以前,go 命令使用高于排除版本的下一个版本,但该版本可能会随时间变化,导致不可重现的构建。

在模块模式下,go 命令现在禁止包含非 ASCII 字符或以点字符 (.) 开头的路径元素的导入路径。带有这些字符的模块路径已被禁止(请参阅模块路径和版本),因此此更改仅影响模块子目录中的路径。

嵌入文件

go 命令现在支持使用新的 //go:embed 指令将静态文件和文件树作为最终可执行文件的一部分。有关详细信息,请参阅新的 embed 包的文档。

go test

使用 go test 时,如果在执行测试函数期间调用 os.Exit(0) 的测试现在将被视为失败。这将有助于捕获测试调用调用 os.Exit(0) 的代码并因此停止运行所有未来测试的情况。如果 TestMain 函数调用 os.Exit(0),这仍然被视为通过测试。

-c-i 标志与未知标志一起使用时,go test 会报告错误。通常,未知标志会传递给测试,但当使用 -c-i 时,测试不会运行。

go get

go get -insecure 标志已被弃用,并将在未来版本中删除。此标志允许使用 HTTP 等不安全方案从存储库获取并解析自定义域,并绕过使用校验和数据库的模块总和验证。要允许使用不安全方案,请改用 GOINSECURE 环境变量。要绕过模块总和验证,请使用 GOPRIVATEGONOSUMDB。有关详细信息,请参阅 go help environment

go get example.com/mod@patch 现在要求主模块已经需要 example.com/mod 的某个版本。(然而,go get -u=patch 即使是新添加的依赖项也会继续修补。)

GOVCS 环境变量

GOVCS 是一个新环境变量,它限制 go 命令可用于下载源代码的版本控制工具。这可以缓解通常在受信任、经过身份验证的环境中使用的工具的安全问题。默认情况下,githg 可用于从任何存储库下载代码。svnbzrfossil 只能用于从模块路径或包路径与 GOPRIVATE 环境变量中的模式匹配的存储库下载代码。有关详细信息,请参阅 go help vcs

all 模式

当主模块的 go.mod 文件声明 go 1.16 或更高版本时,all 包模式现在仅匹配主模块中发现的包或测试传递导入的那些包。(主模块导入的包的*测试*导入的包不再包含在内。)这与自 Go 1.11 以来 go mod vendor 保留的包集相同。

-toolexec 构建标志

当指定 -toolexec 构建标志以在调用 compile 或 asm 等工具链程序时使用程序时,环境变量 TOOLEXEC_IMPORTPATH 现在设置为正在构建的包的导入路径。

-i 构建标志

go buildgo installgo test 接受的 -i 标志现已弃用。-i 标志指示 go 命令安装命令行上命名的包导入的包。自 Go 1.10 引入构建缓存以来,-i 标志对构建时间不再有显著影响,并且当安装目录不可写时,它会导致错误。

list 命令

当指定 -export 标志时,BuildID 字段现在设置为已编译包的构建 ID。这等效于在 go list -exported -f {{.Export}} 上运行 go tool buildid,但无需额外的步骤。

-overlay 标志

-overlay 标志指定一个 JSON 配置文件,其中包含一组文件路径替换。-overlay 标志可与所有构建命令和 go mod 子命令一起使用。它主要用于 gopls 等编辑器工具,以了解源代码文件未保存更改的影响。配置文件将实际文件路径映射到替换文件路径,并且 go 命令及其构建将运行,就像实际文件路径存在并具有替换文件路径提供的内容,或者如果替换文件路径为空则不存在。

Cgo

cgo 工具将不再尝试将 C 结构位域转换为 Go 结构字段,即使它们的大小可以在 Go 中表示。C 位域在内存中出现的顺序是依赖于实现的,因此在某些情况下,cgo 工具产生了静默不正确的结果。

Vet

goroutine 中无效使用 testing.T 的新警告

vet 工具现在警告在测试期间创建的 goroutine 中对 testing.T 方法 Fatal 的无效调用。这还警告对 testing.T 测试或 testing.B 基准上的 FatalfFailNowSkip{,f,Now} 方法的调用。

对这些方法的调用会停止创建的 goroutine 的执行,而不是 Test*Benchmark* 函数。因此,这些方法要求由运行测试或基准函数的 goroutine 调用。例如

func TestFoo(t *testing.T) {
    go func() {
        if condition() {
            t.Fatal("oops") // This exits the inner func instead of TestFoo.
        }
        ...
    }()
}

在创建的 goroutine 中调用 t.Fatal(或类似方法)的代码应重写为使用 t.Error 发出测试失败信号,并使用替代方法(例如使用 return 语句)提前退出 goroutine。前面的例子可以重写为

func TestFoo(t *testing.T) {
    go func() {
        if condition() {
            t.Error("oops")
            return
        }
        ...
    }()
}

新的帧指针警告

vet 工具现在警告 amd64 汇编代码在不保存和恢复 BP 寄存器(帧指针)的情况下破坏它,这违反了调用约定。不保留 BP 寄存器的代码必须修改为根本不使用 BP,或者通过保存和恢复 BP 来保留 BP。保留 BP 的一个简单方法是将帧大小设置为非零值,这将导致生成的序言和尾声为您保留 BP 寄存器。有关示例修复,请参阅 CL 248260

asn1.Unmarshal 的新警告

vet 工具现在警告向 asn1.Unmarshal 错误地传递非指针或 nil 参数。这类似于对 encoding/json.Unmarshalencoding/xml.Unmarshal 的现有检查。

运行时

新的 runtime/metrics 包引入了一个稳定的接口,用于从 Go 运行时读取实现定义的指标。它取代了现有的函数,如 runtime.ReadMemStatsdebug.GCStats,并且明显更通用和高效。有关详细信息,请参阅包文档。

GODEBUG 环境变量设置为 inittrace=1 现在会使运行时为每个包 init 向标准错误输出一行,总结其执行时间和内存分配。此跟踪可用于查找 Go 启动性能中的瓶颈或回归。GODEBUG 文档描述了格式。

在 Linux 上,运行时现在默认立即向操作系统释放内存(使用 MADV_DONTNEED),而不是在操作系统内存压力下延迟释放(使用 MADV_FREE)。这意味着 RSS 等进程级内存统计信息将更准确地反映 Go 进程正在使用的物理内存量。目前使用 GODEBUG=madvdontneed=1 来改善内存监控行为的系统不再需要设置此环境变量。

Go 1.16 修复了竞态检测器和 Go 内存模型之间的一致性问题。竞态检测器现在更精确地遵循内存模型的通道同步规则。因此,检测器现在可能会报告它以前遗漏的竞态。

编译器

编译器现在可以内联带有未标记 for 循环、方法值和类型开关的函数。内联器还可以检测到更多可以内联的间接调用。

链接器

此版本包括对 Go 链接器的额外改进,减少了链接器资源使用(时间和内存)并提高了代码的健壮性/可维护性。这些更改构成了两版本项目下半部分,旨在现代化 Go 链接器

1.16 中的链接器更改将 1.15 的改进扩展到所有受支持的架构/操作系统组合(1.15 的性能改进主要集中在基于 ELF 的操作系统和 amd64 架构上)。对于一组有代表性的大型 Go 程序,链接速度比 1.15 快 20-25%,并且 linux/amd64 的平均内存使用减少 5-15%,其他架构和操作系统有更大的改进。由于更积极的符号剪枝,大多数二进制文件也更小。

在 Windows 上,go build -buildmode=c-shared 现在默认生成 Windows ASLR DLL。ASLR 可以通过 --ldflags=-aslr=false 禁用。

标准库

嵌入文件

新的 embed 包提供对编译期间使用新的 //go:embed directive 嵌入程序中的文件的访问。

文件系统

新的 io/fs 包定义了 fs.FS 接口,它是文件只读树的抽象。标准库包已根据需要进行了调整以使用该接口。

在接口的生产者端,新的 embed.FS 类型实现了 fs.FSzip.Reader 也是如此。新的 os.DirFS 函数提供了一个由操作系统文件树支持的 fs.FS 实现。

在消费者端,新的 http.FS 函数将 fs.FS 转换为 http.FileSystem。此外,html/templatetext/template 包的 ParseFS 函数和方法从 fs.FS 读取模板。

为了测试实现 fs.FS 的代码,新的 testing/fstest 包提供了一个 TestFS 函数,用于检查并报告常见错误。它还提供了一个简单的内存文件系统实现 MapFS,这对于测试接受 fs.FS 实现的代码很有用。

io/ioutil 的弃用

io/ioutil 包已被证明是一个定义不清且难以理解的事物集合。该包提供的所有功能都已移至其他包。io/ioutil 包仍然存在并将继续像以前一样工作,但我们鼓励新代码使用 ioos 包中的新定义。以下是 io/ioutil 导出的名称的新位置列表:

对库的微小更改

与往常一样,库中有各种微小的更改和更新,这些都是在遵守 Go 1 兼容性承诺的前提下进行的。

archive/zip

新的 Reader.Open 方法实现了 fs.FS 接口。

crypto/dsa

crypto/dsa 包现已弃用。请参阅 issue #40337

crypto/hmac

如果哈希生成函数的单独调用未能返回新值,New 现在将 panic。以前,行为是未定义的,有时会生成无效输出。

crypto/tls

现在可以使用新的 net.ErrClosed 错误检测关闭或已关闭 TLS 连接上的 I/O 操作。典型的用法是 errors.Is(err, net.ErrClosed)

Conn.Close 中发送“关闭通知”警报之前,现在设置了默认写入截止时间,以防止无限期阻塞。

如果服务器选择 一个 ALPN 协议,该协议不在 客户端通告的列表 中,客户端现在将返回握手错误。

如果客户端或服务器都没有 AES 硬件支持,服务器现在将优先选择其他可用的 AEAD 密码套件(例如 ChaCha20Poly1305),而不是 AES-GCM 密码套件,除非同时设置了 Config.PreferServerCipherSuitesConfig.CipherSuites。如果客户端没有表示对 AES-GCM 密码套件的偏好,则假定客户端没有 AES 硬件支持。

Config.Clone 现在在接收器为 nil 时返回 nil,而不是 panic。

crypto/x509

GODEBUG=x509ignoreCN=0 标志将在 Go 1.17 中删除。它启用了在不存在主题备用名称时将 X.509 证书上的 CommonName 字段视为主机名的传统行为。

ParseCertificateCreateCertificate 现在对 DNSNamesEmailAddressesURIs 字段强制执行字符串编码限制。这些字段只能包含 ASCII 范围内的字符。

CreateCertificate 现在使用签名者的公钥验证生成的证书签名。如果签名无效,则返回错误,而不是格式错误的证书。

不再支持 DSA 签名验证。请注意,DSA 签名生成从未受支持。请参阅 issue #40337

在 Windows 上,Certificate.Verify 现在将返回由平台证书验证器构建的所有证书链,而不仅仅是排名最高的链。

新的 SystemRootsError.Unwrap 方法允许通过 errors 包函数访问 Err 字段。

在 Unix 系统上,crypto/x509 包现在更有效地存储其系统证书池的副本。只使用少量根的程序将减少大约半兆字节的内存使用。

debug/elf

已添加更多 DTPT 常量。

encoding/asn1

当参数不是指针或为 nil 时,UnmarshalUnmarshalWithParams 现在返回错误而不是 panic。此更改与 encoding/json 等其他编码包的行为一致。

encoding/json

MarshalUnmarshal 和相关功能理解的 json 结构字段标签现在允许 Go 结构字段的 JSON 对象名称中包含分号字符。

encoding/xml

编码器始终注意避免使用以 xml 开头的命名空间前缀,这些前缀是 XML 规范保留的。现在,更严格地遵循规范,该检查不区分大小写,因此也避免了以 XMLXmL 等开头的​​前缀。

flag

新的 Func 函数允许注册通过调用函数实现的标志,作为实现 Value 接口的更轻量级的替代方案。

go/build

Package 结构有新字段,报告包中 //go:embed 指令的信息:EmbedPatternsEmbedPatternPosTestEmbedPatternsTestEmbedPatternPosXTestEmbedPatternsXTestEmbedPatternPos

Package 字段 IgnoredGoFiles 将不再包含以“_”或“.”开头的文件,因为这些文件始终被忽略。IgnoredGoFiles 用于因构建约束而被忽略的文件。

新的 Package 字段 IgnoredOtherFiles 包含因构建约束而被忽略的非 Go 文件列表。

go/build/constraint

新的 go/build/constraint 包解析构建约束行,包括原始的 // +build 语法和将在 Go 1.17 中引入的 //go:build 语法。此包的存在是为了使使用 Go 1.16 构建的工具能够处理 Go 1.17 源代码。有关构建约束语法和计划过渡到 //go:build 语法的详细信息,请参阅 https://golang.ac.cn/design/draft-gobuild。请注意,Go 1.16 **不支持** //go:build 行,并且不应将其引入 Go 程序。

html/template

新的 template.ParseFS 函数和 template.Template.ParseFS 方法类似于 template.ParseGlobtemplate.Template.ParseGlob,但从 fs.FS 读取模板。

io

该包现在定义了一个 ReadSeekCloser 接口。

该包现在定义了 DiscardNopCloserReadAll,用于替代 io/ioutil 包中的同名函数。

log

新的 Default 函数提供对默认 Logger 的访问。

log/syslog

当记录到自定义 Unix 域套接字时,Writer 现在使用本地消息格式(省略主机名并使用较短的时间戳),与默认日志套接字已使用的格式匹配。

mime/multipart

ReaderReadForm 方法在将最大 int64 值作为限制传递时不再拒绝表单数据。

net

关闭的网络连接上的 I/O 操作,或在任何 I/O 完成之前关闭的网络连接上的 I/O 操作,现在可以使用新的 ErrClosed 错误进行检测。典型的用法是 errors.Is(err, net.ErrClosed)。在早期版本中,唯一可靠检测这种情况的方法是将 Error 方法返回的字符串与 "use of closed network connection" 匹配。

在之前的 Go 版本中,Linux 系统上默认的 TCP 侦听器积压大小,由 /proc/sys/net/core/somaxconn 设置,最大限制为 65535。在 Linux 内核版本 4.1 及更高版本中,最大值为 4294967295

在 Linux 上,当 /etc/nsswitch.conf 缺失时,主机名查找不再在检查 /etc/hosts 之前使用 DNS;这在基于 musl 的系统上很常见,并使 Go 程序与这些系统上的 C 程序行为一致。

net/http

net/http 包中,StripPrefix 的行为已更改为除了从请求 URL 的 Path 字段之外,还从其 RawPath 字段中去除前缀。在过去的版本中,只修剪了 Path 字段,因此如果请求 URL 包含任何转义字符,URL 将被修改为具有不匹配的 PathRawPath 字段。在 Go 1.16 中,StripPrefix 修剪这两个字段。如果请求 URL 的前缀部分中有转义字符,则处理程序会返回 404,而不是其以前的旧行为(使用不匹配的 Path/RawPath 对调用底层处理程序)。

net/http 包现在拒绝形式为 "Range": "bytes=--N"(其中 "-N" 是负后缀长度,例如 "Range": "bytes=--2")的 HTTP 范围请求。它现在回复 416 "Range Not Satisfiable" 响应。

使用 SameSiteDefaultMode 设置的 Cookie 现在根据当前规范(未设置属性)而不是生成没有值的 SameSite 键来运行。

Client 现在在带有空主体的 PATCH 请求中发送显式 Content-Length: 0 头,与 POSTPUT 的现有行为匹配。

HTTPS_PROXY 未设置时,ProxyFromEnvironment 函数不再为 https:// URL 返回 HTTP_PROXY 环境变量的设置。

Transport 类型有一个新字段 GetProxyConnectHeader,可以将其设置为一个函数,该函数返回在 CONNECT 请求期间发送到代理的头部。实际上,GetProxyConnectHeader 是现有字段 ProxyConnectHeader 的动态版本;如果 GetProxyConnectHeader 不为 nil,则忽略 ProxyConnectHeader

新的 http.FS 函数将 fs.FS 转换为 http.FileSystem

net/http/httputil

ReverseProxy 现在在代理具有未知主体长度的流式响应时更积极地刷新缓冲数据。

net/smtp

ClientMail 方法现在向支持 SMTPUTF8 指令的服务器发送该指令,表明地址以 UTF-8 编码。

os

当进程已完成时,Process.Signal 现在返回 ErrProcessDone 而不是未导出的 errFinished

该包定义了一个新类型 DirEntry 作为 fs.DirEntry 的别名。新的 ReadDir 函数和新的 File.ReadDir 方法可用于将目录内容读取为 DirEntry 的切片。File.Readdir 方法(注意 dir 中的小写 d)仍然存在,返回 FileInfo 的切片,但对于大多数程序来说,切换到 File.ReadDir 会更高效。

该包现在定义了 CreateTempMkdirTempReadFileWriteFile,用于替代 io/ioutil 包中定义的函数。

类型 FileInfoFileModePathError 现在是 io/fs 包中同名类型的别名。os 包中的函数签名已更新,以引用 io/fs 包中的名称。这不应影响任何现有代码。

新的 DirFS 函数提供了一个由操作系统文件树支持的 fs.FS 实现。

os/signal

新的 NotifyContext 函数允许创建在特定信号到达时取消的上下文。

path

Match 函数现在在模式不匹配部分存在语法错误时返回错误。以前,该函数会在匹配失败时提前返回,因此不会报告模式中任何后续的语法错误。

path/filepath

新函数 WalkDir 类似于 Walk,但通常更高效。传递给 WalkDir 的函数接收一个 fs.DirEntry 而不是一个 fs.FileInfo。(对于那些记得 Walk 函数接受 os.FileInfo 的人来说,os.FileInfo 现在是 fs.FileInfo 的别名。)

MatchGlob 函数现在在模式不匹配部分存在语法错误时返回错误。以前,这些函数会在匹配失败时提前返回,因此不会报告模式中任何后续的语法错误。

reflect

Zero 函数已优化以避免分配。如果代码不正确地使用 == 或 DeepEqual 将返回的 Value 与另一个 Value 进行比较,可能会得到与以前的 Go 版本不同的结果。reflect.Value 的文档描述了如何正确比较两个 Value

runtime/debug

当启用 SetPanicOnFault 时使用的 runtime.Error 值现在可以有一个 Addr 方法。如果该方法存在,它将返回触发故障的内存地址。

strconv

ParseFloat 现在使用 Eisel-Lemire 算法,性能提高了两倍。这也可以加速 encoding/json 等文本格式的解码。

syscall

NewCallbackNewCallbackCDecl 现在正确支持连续具有多个小于 uintptr 大小参数的回调函数。这可能需要更改这些函数的使用,以消除小参数之间的手动填充。

Windows 上的 SysProcAttr 有一个新的 NoInheritHandles 字段,该字段在创建新进程时禁用句柄继承。

Windows 上的 DLLError 现在有一个 Unwrap 方法,用于解包其底层错误。

在 Linux 上,SetgidAllThreadsSyscallAllThreadsSyscall6 可用于在进程中的所有 Go 线程上进行系统调用。这些函数只能由不使用 cgo 的程序使用;如果程序使用 cgo,它们将始终返回 syscall.ENOTSUP

testing/iotest

新的 ErrReader 函数返回一个始终返回错误的 io.Reader

新的 TestReader 函数测试 io.Reader 的行为是否正确。

text/template

现在,操作分隔符内允许换行符,从而允许操作跨多行。

新的 template.ParseFS 函数和 template.Template.ParseFS 方法类似于 template.ParseGlobtemplate.Template.ParseGlob,但从 fs.FS 读取模板。

text/template/parse

解析树中添加了一个新的 CommentNodeparse.Tree 中的 Mode 字段允许访问它。

time/tzdata

$GOROOT/lib/time/zoneinfo.zip 中的时区数据库和此包中的嵌入副本现在使用精简时区数据格式。这使时区数据库的大小减少了约 350 KB。

unicode

unicode 包和整个系统中的相关支持已从 Unicode 12.0.0 升级到 Unicode 13.0.0,后者增加了 5,930 个新字符,包括四个新脚本和 55 个新表情符号。Unicode 13.0.0 还将平面 3 (U+30000-U+3FFFF) 指定为第三表意平面。