Go 1.15 发行说明

Go 1.15 简介

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

Go 1.15 包括对链接器的重大改进,改善了高核数下小对象的分配,并弃用了X.509 CommonNameGOPROXY 现在支持跳过返回错误的代理,并且添加了一个新的嵌入式 tzdata 包

语言变化

语言没有变化。

移植

Darwin

正如 Go 1.14 发行说明中宣布的,Go 1.15 需要 macOS 10.12 Sierra 或更高版本;对以前版本的支持已停止。

正如 Go 1.14 发行说明中宣布的,Go 1.15 放弃了对 macOS、iOS、iPadOS、watchOS 和 tvOS 上的 32 位二进制文件的支持(darwin/386darwin/arm 端口)。Go 继续支持 64 位 darwin/amd64darwin/arm64 端口。

Windows

当提供了 -buildmode=pie cmd/link 标志时,Go 现在会生成 Windows ASLR 可执行文件。Go 命令在 Windows 上默认使用 -buildmode=pie

-race-msan 标志现在总是启用 -d=checkptr,它会检查 unsafe.Pointer 的使用。以前除了 Windows 之外的所有操作系统都是这样。

Go 构建的 DLL 不再在收到信号(例如终端中的 Ctrl-C)时导致进程退出。

安卓

链接 Android 二进制文件时,Go 1.15 显式选择 NDK 最新版本中可用的 lld 链接器。lld 链接器可以避免某些设备上的崩溃,并计划在未来的 NDK 版本中成为默认的 NDK 链接器。

OpenBSD

Go 1.15 添加了对 GOARCH=armGOARCH=arm64 上 OpenBSD 6.7 的支持。Go 的早期版本已经支持 GOARCH=386GOARCH=amd64 上 OpenBSD 6.7。

RISC-V

在改进 Linux 上 64 位 RISC-V 端口(GOOS=linuxGOARCH=riscv64)的稳定性和性能方面取得了进展。它现在还支持异步抢占。

386

Go 1.15 是最后一个支持仅 x87 浮点硬件(GO386=387)的版本。未来的版本将要求 386 上至少支持 SSE2,将 Go 的最低 GOARCH=386 要求提高到 Intel Pentium 4(2000 年发布)或 AMD Opteron/Athlon 64(2003 年发布)。

工具

Go 命令

GOPROXY 环境变量现在支持跳过返回错误的代理。代理 URL 现在可以用逗号 (,) 或竖线字符 (|) 分隔。如果代理 URL 后跟逗号,则 go 命令只会在 HTTP 响应为 404 或 410 后尝试列表中的下一个代理。如果代理 URL 后跟竖线字符,则 go 命令会在任何错误后尝试列表中的下一个代理。请注意,GOPROXY 的默认值仍为 https://proxy.golang.org,direct,它在出现错误时不会回退到 direct

go test

更改 -timeout 标志现在会使缓存的测试结果失效。当使用较短的超时重新调用 go test 时,使用较长超时运行的测试的缓存结果将不再算作通过。

标志解析

go testgo vet 中的各种标志解析问题已得到修复。值得注意的是,GOFLAGS 中指定的标志处理得更加一致,并且 -outputdir 标志现在将相对路径解释为相对于 go 命令的工作目录(而不是每个单独测试的工作目录)。

模块缓存

模块缓存的位置现在可以使用 GOMODCACHE 环境变量设置。GOMODCACHE 的默认值为 GOPATH[0]/pkg/mod,这是此更改之前模块缓存的位置。

现在可以使用一种解决方法来解决 go 命令访问模块缓存时出现的 Windows“访问被拒绝”错误,该错误由外部程序同时扫描文件系统引起(请参阅问题 #36568)。默认情况下不启用该解决方法,因为它在 Go 1.14.2 和 1.13.10 以下版本与同一模块缓存同时运行时不安全。可以通过显式设置环境变量 GODEBUG=modcacheunzipinplace=1 来启用它。

Vet

string(x) 的新警告

vet 工具现在会警告 string(x) 形式的转换,其中 x 的整数类型不是 runebyte。Go 的经验表明,这种形式的许多转换错误地假设 string(x) 求值为整数 x 的字符串表示。它实际上求值为包含 x 值 UTF-8 编码的字符串。例如,string(9786) 不求值为字符串 "9786";它求值为字符串 "\xe2\x98\xba",或 "☺"

正确使用 string(x) 的代码可以重写为 string(rune(x))。或者,在某些情况下,使用合适的字节切片 buf 调用 utf8.EncodeRune(buf, x) 可能是正确的解决方案。其他代码很可能应该使用 strconv.Itoafmt.Sprint

使用 go test 时,此新的 vet 检查默认启用。

我们正在考虑在 Go 的未来版本中禁止此转换。也就是说,语言将改变为仅允许 string(x) 用于整数 x,当 x 的类型为 runebyte 时。这种语言更改将不向后兼容。我们正在使用此 vet 检查作为更改语言的第一步。

不可能的接口转换的新警告

vet 工具现在会警告从一种接口类型到另一种接口类型的类型断言,当类型断言总是失败时。如果两个接口类型实现一个同名但具有不同类型签名的方法,就会发生这种情况。

没有理由编写总是失败的类型断言,因此任何触发此 vet 检查的代码都应该重写。

使用 go test 时,此新的 vet 检查默认启用。

我们正在考虑在 Go 的未来版本中禁止不可能的接口类型断言。这种语言更改将不向后兼容。我们正在使用此 vet 检查作为更改语言的第一步。

运行时

如果 panic 调用时传入的值的类型派生自以下任何一种:boolcomplex64complex128float32float64intint8int16int32int64stringuintuint8uint16uint32uint64uintptr,则将打印该值,而不仅仅是其地址。以前,这仅适用于精确这些类型的值。

在 Unix 系统上,如果使用 kill 命令或 kill 系统调用向 Go 程序发送 SIGSEGVSIGBUSSIGFPE 信号,并且如果该信号未通过 os/signal.Notify 处理,Go 程序现在将可靠地崩溃并带有堆栈跟踪。在早期版本中,行为是不可预测的。

在高核数下,小对象的分配现在表现得好得多,并且最坏情况下的延迟更低。

将小整数值转换为接口值不再导致分配。

在关闭通道上的非阻塞接收现在与在打开通道上的非阻塞接收一样好。

编译器

unsafe 包的安全规则允许在调用某些函数时将 unsafe.Pointer 转换为 uintptr。以前,在某些情况下,编译器允许多个链式转换(例如,syscall.Syscall(…, uintptr(uintptr(ptr)), …))。编译器现在要求正好一个转换。使用多个转换的代码应更新以满足安全规则。

Go 1.15 通过消除某些类型的 GC 元数据和更积极地消除未使用的类型元数据,将典型二进制文件大小比 Go 1.14 减少了约 5%。

工具链现在通过将函数对齐到 32 字节边界并填充跳转指令来缓解 GOARCH=amd64 上的 Intel CPU 错误 SKX102。虽然这种填充增加了二进制文件大小,但这被上面提到的二进制文件大小改进所弥补。

Go 1.15 为编译器和汇编器添加了 -spectre 标志,以允许启用 Spectre 缓解措施。这些几乎从不需要,主要作为“纵深防御”机制提供。有关详细信息,请参阅 Spectre wiki 页面

编译器现在会拒绝与所应用的声明无关的 //go: 编译器指令,并报告“ misplaced compiler directive ”错误。此类错误应用的指令以前是错误的,但被编译器静默忽略。

编译器的 -json 优化日志现在报告大型(>= 128 字节)副本,并包括逃逸分析决策的解释。

链接器

此版本包括对 Go 链接器的重大改进,这些改进减少了链接器资源使用(时间和内存)并提高了代码的健壮性/可维护性。

对于一组具有代表性的大型 Go 程序,在 amd64 架构上运行的基于 ELF 的操作系统(Linux、FreeBSD、NetBSD、OpenBSD、Dragonfly 和 Solaris)上,链接速度平均快 20%,内存需求平均减少 30%,对于其他架构/操作系统组合,改进则较为温和。

链接器性能更好的关键因素是重新设计了目标文件格式,以及重组了内部阶段以增加并发性(例如,并行应用重定位到符号)。Go 1.15 中的目标文件略大于其 1.14 等效文件。

这些更改是现代化 Go 链接器的多版本项目的一部分,这意味着未来版本中将有更多链接器改进。

链接器现在在 linux/amd64linux/arm64 上,对于 -buildmode=pie 默认为内部链接模式,因此这些配置不再需要 C 链接器。外部链接模式(Go 1.14 中对于 -buildmode=pie 是默认值)仍然可以通过 -ldflags=-linkmode=external 标志请求。

Objdump

objdump 工具现在支持使用 -gnu 标志以 GNU 汇编器语法反汇编。

标准库

新的嵌入式 tzdata 包

Go 1.15 包含一个新包 time/tzdata,它允许将时区数据库嵌入到程序中。导入此包(作为 import _ "time/tzdata")允许程序即使在本地系统上没有时区数据库也能找到时区信息。您也可以通过使用 -tags timetzdata 构建来嵌入时区数据库。这两种方法都会使程序大小增加约 800 KB。

Cgo

Go 1.15 会将 C 类型 EGLConfig 转换为 Go 类型 uintptr。此更改类似于 Go 1.12 及更高版本处理 EGLDisplay、Darwin 的 CoreFoundation 和 Java 的 JNI 类型的方式。有关更多信息,请参阅 cgo 文档

在 Go 1.15.3 及更高版本中,cgo 将不允许 Go 代码在堆栈或堆上分配未定义的结构类型(仅定义为 struct S; 或类似的 C 结构)。Go 代码只允许使用指向这些类型的指针。分配此类结构实例并将指针或完整的结构值传递给 C 代码始终是不安全的,并且不太可能正常工作;现在被禁止。修复方法是重写 Go 代码以仅使用指针,或者通过包含适当的 C 头文件来确保 Go 代码看到结构的完整定义。

X.509 CommonName 弃用

当不存在 Subject Alternative Names 时,将 X.509 证书上的 CommonName 字段视为主机名的已弃用、旧式行为现已默认禁用。可以通过将值 x509ignoreCN=0 添加到 GODEBUG 环境变量中来暂时重新启用它。

请注意,如果 CommonName 是无效的主机名,它总是会被忽略,无论 GODEBUG 设置如何。无效名称包括那些包含除字母、数字、连字符和下划线之外的任何字符的名称,以及那些带有空标签或尾随点的名称。

对库的微小更改

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

bufio

Scanner 与一个错误地从 Read 返回负数的无效 io.Reader 一起使用时,Scanner 不再会发生 panic,而是返回新的错误 ErrBadReadCount

context

现在明确禁止使用 nil 父级创建派生的 Context。任何尝试使用 WithValueWithDeadlineWithCancel 函数执行此操作都会导致 panic。

crypto

crypto/rsacrypto/ecdsacrypto/ed25519 包中的 PrivateKeyPublicKey 类型现在具有一个 Equal 方法,用于比较密钥是否等效或为公钥创建类型安全接口。该方法签名与 go-cmp 的相等定义兼容。

Hash 现在实现了 fmt.Stringer

crypto/ecdsa

新的 SignASN1VerifyASN1 函数允许以标准 ASN.1 DER 编码生成和验证 ECDSA 签名。

crypto/elliptic

新的 MarshalCompressedUnmarshalCompressed 函数允许以压缩格式编码和解码 NIST 椭圆曲线点。

crypto/rsa

VerifyPKCS1v15 现在根据 RFC 8017 拒绝缺少前导零的无效短签名。

crypto/tls

新的 Dialer 类型及其 DialContext 方法允许使用上下文连接和与 TLS 服务器握手。

Config 类型上的新 VerifyConnection 回调允许对每个连接进行自定义验证逻辑。它可以访问 ConnectionState,其中包括对等证书、SCT 和附加的 OCSP 响应。

自动生成的会话票证密钥现在每 24 小时自动轮换一次,有效期为 7 天,以限制它们对前向保密性的影响。

TLS 1.2 及更早版本中会话票证的有效期(其中会话密钥用于恢复连接)现在限制为 7 天,同样是为了限制它们对前向保密性的影响。

现在强制执行 RFC 8446 中规定的客户端降级保护检查。这可能会导致客户端遇到表现为未经授权的降级攻击的中间件时出现连接错误。

SignatureSchemeCurveIDClientAuthType 现在实现了 fmt.Stringer

客户端恢复连接时,ConnectionState 字段 OCSPResponseSignedCertificateTimestamps 现在会重新填充。

tls.Conn 现在对永久性损坏的连接返回一个不透明错误,包装了临时的 net.Error。要访问原始的 net.Error,请使用 errors.As(或 errors.Unwrap)而不是类型断言。

crypto/x509

如果证书上的名称或正在验证的名称(使用 VerifyOptions.DNSNameVerifyHostname)无效,它们现在将不区分大小写地进行比较,不进行进一步处理(不遵守通配符或去除尾随点)。无效名称包括那些包含除字母、数字、连字符和下划线之外的任何字符的名称,那些带有空标签的名称,以及证书上带有尾随点的名称。

新的 CreateRevocationList 函数和 RevocationList 类型允许创建符合 RFC 5280 的 X.509 v2 证书吊销列表。

如果模板是 CA 并且未明确指定 SubjectKeyId,则 CreateCertificate 现在会自动生成它。

如果模板指定 MaxPathLen 但不是 CA,则 CreateCertificate 现在会返回错误。

在 macOS 以外的 Unix 系统上,SSL_CERT_DIR 环境变量现在可以是冒号分隔的列表。

在 macOS 上,二进制文件现在总是与 Security.framework 链接,以提取系统信任根,无论 cgo 是否可用。由此产生的行为应与操作系统验证器更一致。

crypto/x509/pkix

如果 ExtraNames 为 nil,则 Name.String 现在会打印 Names 中的非标准属性。

database/sql

新的 DB.SetConnMaxIdleTime 方法允许在连接空闲一段时间后将其从连接池中移除,而不考虑连接的总寿命。DBStats.MaxIdleTimeClosed 字段显示由于 DB.SetConnMaxIdleTime 关闭的连接总数。

新的 Row.Err getter 允许在不调用 Row.Scan 的情况下检查查询错误。

database/sql/driver

Conn 可以实现新的 Validator 接口,以允许驱动程序指示连接是否有效或是否应丢弃。

debug/pe

该包现在定义了 PE 文件格式使用的 IMAGE_FILEIMAGE_SUBSYSTEMIMAGE_DLLCHARACTERISTICS 常量。

encoding/asn1

Marshal 现在根据 X.690 DER 对 SET OF 的组件进行排序。

Unmarshal 现在拒绝不符合 X.690 DER 最小编码的标签和对象标识符。

encoding/json

该包现在在解码时对最大嵌套深度有一个内部限制。这减少了深度嵌套的输入可能使用大量堆栈内存,甚至导致“goroutine stack exceeds limit”恐慌的可能性。

flag

flag 包看到 -h-help,并且这些标志未定义时,它现在会打印用法消息。如果 FlagSet 是使用 ExitOnError 创建的,则 FlagSet.Parse 将以状态 2 退出。在此版本中,-h-help 的退出状态已更改为 0。特别是,这适用于命令行标志的默认处理。

fmt

打印动词 %#g%#G 现在保留浮点值的尾随零。

go/format

SourceNode 函数现在在格式化 Go 源代码时将数字字面量前缀和指数规范化。这与 自 Go 1.13 以来实现的 gofmt 命令的行为一致。

html/template

该包现在在所有 JavaScript 和 JSON 上下文中使用 Unicode 转义 (\uNNNN)。这修复了 application/ld+jsonapplication/json 上下文中的转义错误。

io/ioutil

TempDirTempFile 现在拒绝包含路径分隔符的模式。也就是说,像 ioutil.TempFile("/tmp", "../base*") 这样的调用将不再成功。这可以防止意外的目录遍历。

math/big

新的 Int.FillBytes 方法允许序列化到固定大小的预分配字节切片。

math/cmplx

此包中的函数已更新,以符合 C99 标准(附录 G IEC 60559 兼容的复数算法),涉及处理特殊参数,例如无穷大、NaN 和带符号的零。

net

如果 I/O 操作超出由 Conn.SetDeadlineConn.SetReadDeadlineConn.SetWriteDeadline 方法设置的截止时间,它现在将返回一个等同于或包装 os.ErrDeadlineExceeded 的错误。这可用于可靠地检测错误是否是由于超出截止时间造成的。早期版本建议在错误上调用 Timeout 方法,但 I/O 操作可以返回错误,即使未超出截止时间,Timeout 也返回 true

新的 Resolver.LookupIP 方法支持特定于网络且接受上下文的 IP 查找。

net/http

解析现在更严格,作为针对请求走私攻击的强化措施:非 ASCII 空格不再像 SP 和 HTAB 一样被修剪,并且放弃了对“identityTransfer-Encoding 的支持。

net/http/httputil

ReverseProxy 现在支持在 Request.Header 映射中该字段的传入条目为 nil 时不修改 X-Forwarded-For 标头。

当由 ReverseProxy 处理的切换协议(如 WebSocket)请求被取消时,后端连接现在已正确关闭。

net/http/pprof

所有配置文件端点现在都支持一个“seconds”参数。当存在时,端点会为指定的秒数进行配置文件分析并报告差异。cpu 配置文件和跟踪端点中“seconds”参数的含义不变。

net/url

新的 URL 字段 RawFragment 和方法 EscapedFragment 提供了有关特定片段精确编码的详细信息和控制。这些类似于 RawPathEscapedPath

新的 URL 方法 Redacted 返回以字符串形式表示的 URL,其中任何密码都替换为 xxxxx

os

如果 I/O 操作超出由 File.SetDeadlineFile.SetReadDeadlineFile.SetWriteDeadline 方法设置的截止时间,它现在将返回一个等同于或包装 os.ErrDeadlineExceeded 的错误。这可用于可靠地检测错误是否是由于超出截止时间造成的。早期版本建议在错误上调用 Timeout 方法,但 I/O 操作可以返回错误,即使未超出截止时间,Timeout 也返回 true

osnet 包现在会自动重试因 EINTR 失败的系统调用。以前这会导致虚假故障,在 Go 1.14 中添加异步抢占后变得更加常见。现在这已透明处理。

os.File 类型现在支持 ReadFrom 方法。这允许在某些系统上使用 io.Copy 将数据从一个 os.File 复制到另一个 os.File 时使用 copy_file_range 系统调用。结果是 io.CopyBuffer 在复制到 os.File 时不总是使用提供的缓冲区。如果程序想要强制使用提供的缓冲区,可以通过写入 io.CopyBuffer(struct{ io.Writer }{dst}, src, buf) 来完成。

插件

在 macOS 上,对于 -buildmode=plugin,现在支持 DWARF 生成(并且默认启用)。

freebsd/amd64 上,现在支持使用 -buildmode=plugin 进行构建。

reflect

reflect 包现在禁止访问所有非导出字段的方法,而以前它允许访问非导出、嵌入字段的方法。依赖于先前行为的代码应更新为转而访问 enclosing 变量的相应提升方法。

regexp

新的 Regexp.SubexpIndex 方法返回给定名称的第一个子表达式在正则表达式中的索引。

runtime

包括 ReadMemStatsGoroutineProfile 在内的几个函数,如果正在进行垃圾回收,则不再阻塞。

runtime/pprof

goroutine 配置文件现在包括分析时与每个 goroutine 关联的配置文件标签。此功能尚未在 debug=2 报告的配置文件中实现。

strconv

添加了 FormatComplexParseComplex 用于处理复数。

FormatComplex 将复数转换为 (a+bi) 形式的字符串,其中 a 和 b 是实部和虚部。

ParseComplex 将字符串转换为指定精度的复数。ParseComplex 接受 N+Ni 格式的复数。

sync

新的方法 Map.LoadAndDelete 原子地删除一个键并返回先前的存在值(如果存在)。

方法 Map.Delete 更高效。

syscall

在 Unix 系统上,使用 SysProcAttr 的函数现在将拒绝同时设置 SetcttyForeground 字段的尝试,因为它们都使用 Ctty 字段但以不兼容的方式。我们预计很少有现有程序会同时设置这两个字段。

设置 Setctty 字段现在要求 Ctty 字段设置为子进程中的文件描述符号,由 ProcAttr.Files 字段确定。使用子描述符始终有效,但在某些情况下,使用父文件描述符也恰好有效。一些设置 Setctty 的程序将需要更改 Ctty 的值以使用子描述符号。

现在可以在 windows/amd64 上调用返回浮点值的系统调用

testing

testing.T 类型现在有一个 Deadline 方法,报告测试二进制文件将超出其超时的时间。

TestMain 函数不再需要调用 os.Exit。如果 TestMain 函数返回,测试二进制文件将使用 m.Run 返回的值调用 os.Exit

新的方法 T.TempDirB.TempDir 返回临时目录,这些目录在测试结束时会自动清理。

go test -v 现在按测试名称对输出进行分组,而不是在每行上打印测试名称。

text/template

JSEscape 现在始终使用 Unicode 转义 (\u00XX),这与 JSON 兼容。

time

新的方法 Ticker.Reset 支持更改计时器的持续时间。

返回错误时,ParseDuration 现在会引用原始值。