Go 1.12 发行说明

Go 1.12 简介

最新的 Go 版本 1.12 在 Go 1.11 发布六个月后推出。它的主要变化在于工具链、运行时和库的实现。与以往一样,此版本保持了 Go 1 的兼容性承诺。我们预计几乎所有 Go 程序都能像以前一样继续编译和运行。

语言变化

语言规范没有变化。

移植

竞态检测器现在支持 linux/arm64

Go 1.12 是支持 FreeBSD 10.x 的最后一个版本,该版本已达到生命周期结束。Go 1.13 将需要 FreeBSD 11.2+ 或 FreeBSD 12.0+。FreeBSD 12.0+ 需要一个设置了 COMPAT_FREEBSD11 选项的内核(这是默认设置)。

cgo 现在支持 linux/ppc64

hurd 现在是 GOOS 的一个识别值,保留用于 GNU/Hurd 系统,与 gccgo 配合使用。

Windows

Go 的新 windows/arm 端口支持在树莓派 3 等 32 位 ARM 芯片上的 Windows 10 IoT Core 上运行 Go。

AIX

Go 现在支持 POWER8 架构上的 AIX 7.2 及更高版本 (aix/ppc64)。外部链接、cgo、pprof 和竞态检测器尚未支持。

Darwin

Go 1.12 是将在 macOS 10.10 Yosemite 上运行的最后一个版本。Go 1.13 将需要 macOS 10.11 El Capitan 或更高版本。

在 Darwin 上进行系统调用时,现在使用 libSystem,确保与未来版本的 macOS 和 iOS 的向前兼容性。切换到 libSystem 触发了对私有 API 使用的额外 App Store 检查。由于它被视为私有,syscall.Getdirentries 现在在 iOS 上总是以 ENOSYS 失败。此外,syscall.Setrlimit 在以前成功的某些地方报告 invalid argument。这些后果并非 Go 特有,用户应期望与 libSystem 的实现保持行为一致。

工具

go tool vet 不再支持

go vet 命令已被重写,作为一系列不同源代码分析工具的基础。有关详细信息,请参阅 golang.org/x/tools/go/analysis 包。其副作用是 go tool vet 不再受支持。使用 go tool vet 的外部工具必须更改为使用 go vet。使用 go vet 而不是 go tool vet 应该适用于所有受支持的 Go 版本。

作为此更改的一部分,实验性的 -shadow 选项不再与 go vet 一起使用。现在可以使用以下命令检查变量遮蔽:

go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)

教程

Go 教程不再包含在主二进制分发中。要在本地运行教程,请手动安装它,而不是运行 go tool tour

go get -u golang.org/x/tour
tour

构建缓存要求

构建缓存现在是消除 $GOPATH/pkg 的一步。设置环境变量 GOCACHE=off 将导致写入缓存的 go 命令失败。

仅限二进制包

Go 1.12 是最后一个支持仅限二进制包的版本。

Cgo

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

使用 Cgo 的包中不再接受被修改的 C 名称。请改用 Cgo 名称。例如,使用文档化的 cgo 名称 C.char,而不是 cgo 生成的被修改名称 _Ctype_char

模块

GO111MODULE 设置为 on 时,go 命令现在支持在模块目录之外进行模块感知操作,前提是这些操作不需要解析相对于当前目录的导入路径或显式编辑 go.mod 文件。诸如 go getgo listgo mod download 等命令的行为就像在一个初始为空的模块中一样。在此模式下,go env GOMOD 报告系统的空设备(/dev/nullNUL)。

下载和提取模块的 go 命令现在可以安全地并发调用。模块缓存 (GOPATH/pkg/mod) 必须位于支持文件锁定的文件系统中。

go.mod 文件中的 go 指令现在指示该模块中文件使用的语言版本。如果不存在现有版本,它将设置为当前版本 (go 1.12)。如果模块的 go 指令指定了比正在使用的工具链更新的版本,go 命令将尝试构建包,并且只有在构建失败时才会指出不匹配。

go 指令的这种更改意味着,如果您使用 Go 1.12 构建一个模块,从而在 go.mod 文件中记录 go 1.12,那么在尝试使用 Go 1.11 到 Go 1.11.3 构建同一模块时将收到错误。Go 1.11.4 或更高版本将正常工作,早于 Go 1.11 的版本也将正常工作。如果您必须使用 Go 1.11 到 1.11.3,您可以通过使用 Go 1.12 go 工具,通过 go mod edit -go=1.11 将语言版本设置为 1.11 来避免此问题。

当无法使用活动模块解析导入时,go 命令现在将尝试使用主模块的 replace 指令中提到的模块,然后再查询模块缓存和通常的网络源。如果找到匹配的替换但 replace 指令未指定版本,则 go 命令使用派生自零 time.Time 的伪版本(例如 v0.0.0-00010101000000-000000000000)。

编译器工具链

编译器的活跃变量分析已改进。这可能意味着在此版本中,终结器将比以前的版本更早执行。如果这是一个问题,请考虑适当添加 runtime.KeepAlive 调用。

现在默认情况下,更多函数(包括只调用另一个函数的函数)有资格进行内联。这种额外的内联使得使用 runtime.CallersFrames 而不是直接迭代 runtime.Callers 的结果变得更加重要。

// Old code which no longer works correctly (it will miss inlined call frames).
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
for _, pc := range pcs[:n] {
    f := runtime.FuncForPC(pc)
    if f != nil {
        fmt.Println(f.Name())
    }
}
// New code which will work correctly.
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
for {
    frame, more := frames.Next()
    fmt.Println(frame.Function)
    if !more {
        break
    }
}

由编译器生成的用于实现方法表达式的包装器不再由 runtime.CallersFramesruntime.Stack 报告。它们也不会在 panic 堆栈跟踪中打印。此更改使 gc 工具链与 gccgo 工具链保持一致,后者已从堆栈跟踪中省略了此类包装器。这些 API 的客户端可能需要调整以适应缺失的帧。对于必须在 1.11 和 1.12 版本之间互操作的代码,可以将方法表达式 x.M 替换为函数字面量 func (...) { x.M(...) }

编译器现在接受 -lang 标志来设置要使用的 Go 语言版本。例如,-lang=go1.8 会导致编译器在程序使用类型别名(Go 1.9 中添加)时发出错误。在 Go 1.12 之前进行的语言更改未始终强制执行。

编译器工具链现在使用不同的约定来调用 Go 函数和汇编函数。这对于用户来说应该是不可见的,除了同时跨越 Go 和汇编以及跨越包边界的调用。如果链接导致“relocation target not defined for ABIInternal (but is defined for ABI0)”之类的错误,请参阅 ABI 设计文档的兼容性部分

编译器生成的 DWARF 调试信息有许多改进,包括参数打印和变量位置信息的改进。

Go 程序现在还在 linux/arm64 上维护堆栈帧指针,以利于 perf 等分析工具。帧指针维护会产生少量运行时开销,开销因情况而异,但平均约为 3%。要构建不使用帧指针的工具链,请在运行 make.bash 时设置 GOEXPERIMENT=noframepointer

过时的“安全”编译器模式(由 -u gcflag 启用)已删除。

godocgo doc

在 Go 1.12 中,godoc 不再具有命令行界面,而仅是一个 Web 服务器。用户应该使用 go doc 获取命令行帮助输出。Go 1.12 是最后一个包含 godoc Web 服务器的版本;在 Go 1.13 中,它将通过 go get 提供。

go doc 现在支持 -all 标志,它将打印所有导出的 API 及其文档,就像以前的 godoc 命令行一样。

go doc 现在还包含 -src 标志,它将显示目标的源代码。

Trace

跟踪工具现在支持绘制变异器利用率曲线,包括对执行跟踪的交叉引用。这些对于分析垃圾收集器对应用程序延迟和吞吐量的影响非常有用。

汇编器

arm64 上,平台寄存器从 R18 重命名为 R18_PLATFORM,以防止意外使用,因为操作系统可以选择保留此寄存器。

运行时

当堆的大部分仍然存活时,Go 1.12 显著提高了清扫性能。这减少了垃圾收集后立即的分配延迟。

Go 运行时现在更积极地将内存释放回操作系统,特别是在无法重用现有堆空间的大量分配之后。

Go 运行时的计时器和截止时间代码更快,并且在 CPU 数量更多时扩展性更好。特别是,这提高了操纵网络连接截止时间的性能。

在 Linux 上,运行时现在使用 MADV_FREE 释放未使用的内存。这更有效率,但可能导致报告的 RSS 更高。内核将在需要时回收未使用的​​数据。要恢复到 Go 1.11 的行为 (MADV_DONTNEED),请设置环境变量 GODEBUG=madvdontneed=1

将 cpu.extension=off 添加到 GODEBUG 环境变量现在会禁用标准库和运行时中可选 CPU 指令集扩展的使用。这在 Windows 上尚不支持。

Go 1.12 通过修复大量堆分配的过度计数,提高了内存配置文件的准确性。

堆栈回溯、runtime.Callerruntime.Callers 不再包含编译器生成的初始化函数。在全局变量初始化期间进行堆栈回溯现在将显示一个名为 PKG.init.ializers 的函数。

标准库

TLS 1.3

Go 1.12 在 crypto/tls 包中添加了可选的 TLS 1.3 支持,具体由 RFC 8446 指定。可以通过将值 tls13=1 添加到 GODEBUG 环境变量来启用它。它将在 Go 1.13 中默认启用。

要协商 TLS 1.3,请确保您没有在 Config 中设置显式 MaxVersion,并使用环境变量 GODEBUG=tls13=1 运行您的程序。

除了 ConnectionState 中的 TLSUnique 和重新协商之外,所有 TLS 1.2 功能在 TLS 1.3 中都可用,并提供等效或更好的安全性和性能。请注意,尽管 TLS 1.3 向后兼容以前的版本,但某些旧系统在尝试协商它时可能无法正常工作。过小而不安全的 RSA 证书密钥(包括 512 位密钥)将无法与 TLS 1.3 配合使用。

TLS 1.3 密码套件不可配置。所有支持的密码套件都是安全的,如果在 Config 中设置了 PreferServerCipherSuites,则偏好顺序基于可用的硬件。

目前不支持作为客户端或服务器的早期数据(也称为“0-RTT 模式”)。此外,Go 1.12 服务器不支持在客户端发送意外早期数据时跳过。由于 TLS 1.3 0-RTT 模式涉及客户端保留有关哪些服务器支持 0-RTT 的状态,Go 1.12 服务器不能成为负载均衡池的一部分,其中一些其他服务器确实支持 0-RTT。如果将域从支持 0-RTT 的服务器切换到 Go 1.12 服务器,则必须在切换之前至少在颁发的会话票证的生命周期内禁用 0-RTT,以确保不间断运行。

在 TLS 1.3 中,客户端是握手中最后发言的一方,因此如果它导致服务器上发生错误,该错误将由第一次 Read 在客户端上返回,而不是由 Handshake 返回。例如,如果服务器拒绝客户端证书,就会出现这种情况。同样,会话票证现在是握手后消息,因此只有在客户端第一次 Read 时才会收到。

对库的微小更改

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

bufio

如果 Peek 之后调用 ReaderUnreadRuneUnreadByte 方法,它们现在将返回错误。

bytes

新函数 ReplaceAll 返回一个字节切片的副本,其中所有非重叠的值实例都被另一个值替换。

零值 Reader 的指针现在在功能上等同于 NewReader(nil)。在 Go 1.12 之前,前者在所有情况下都不能替代后者。

crypto/rand

Reader.Read 首次阻塞超过 60 秒等待从内核读取熵时,将打印警告到标准错误。

在 FreeBSD 上,Reader 现在使用 getrandom 系统调用(如果可用),否则使用 /dev/urandom

crypto/rc4

此版本删除了汇编实现,只留下纯 Go 版本。Go 编译器生成的代码略好或略差,具体取决于 CPU。RC4 不安全,应仅用于与旧系统兼容。

crypto/tls

如果客户端发送的初始消息看起来不像 TLS,服务器将不再回复警报,并且它将在 RecordHeaderError 的新字段 Conn 中公开底层 net.Conn

database/sql

现在可以通过将 *Rows 值传递给 Row.Scan 方法来获取查询游标。

expvar

新的 Delete 方法允许从 Map 中删除键/值对。

fmt

现在以按键排序的顺序打印映射,以方便测试。排序规则如下:

  • 如果适用,nil 比较为低
  • 整数、浮点数和字符串按 < 排序
  • NaN 小于非 NaN 浮点数
  • 布尔值 false 在 true 之前
  • 复数先比较实部,再比较虚部
  • 指针按机器地址比较
  • 通道值按机器地址比较
  • 结构体依次比较每个字段
  • 数组依次比较每个元素
  • 接口值首先根据描述具体类型的 reflect.Type 进行比较,然后根据前面规则描述的具体值进行比较。

打印映射时,非自反键值(如 NaN)以前显示为 <nil>。从这个版本开始,会打印正确的值。

go/doc

为了解决 cmd/doc 中的一些悬而未决的问题,此包有一个新的 ModePreserveAST,它控制是否清除 AST 数据。

go/token

File 类型有一个新的 LineStart 字段,它返回给定行的起始位置。这在偶尔处理非 Go 文件(例如汇编)但希望使用 token.Pos 机制识别文件位置的程序中特别有用。

图像

RegisterFormat 函数现在可以安全地并发使用。

image/png

颜色少于 16 种的调色板图像现在编码为更小的输出。

io

新的 StringWriter 接口包装了 WriteString 函数。

math

函数 SinCosTanSincos 现在对巨大的参数应用 Payne-Hanek 范围缩减。这会产生更准确的结果,但它们不会与早期版本的结果逐位相同。

math/bits

新的扩展精度操作 AddSubMulDiv 可用于 uintuint32uint64 版本。

net

Dialer.DualStack 设置现在被忽略并已弃用;RFC 6555 快速回退(“Happy Eyeballs”)现在默认启用。要禁用,请将 Dialer.FallbackDelay 设置为负值。

同样,如果 Dialer.KeepAlive 为零,TCP 保活现在默认启用。要禁用,请将其设置为负值。

在 Linux 上,当从 UnixConn 复制到 TCPConn 时,现在使用 splice 系统调用

net/http

HTTP 服务器现在会以纯文本“400 Bad Request”响应拒绝发送到 HTTPS 服务器的误导性 HTTP 请求。

新的 Client.CloseIdleConnections 方法会调用 Client 底层 TransportCloseIdleConnections(如果存在)。

Transport 不再拒绝声明 HTTP Trailers 但不使用分块编码的 HTTP 响应。相反,声明的 Trailers 现在只是被忽略。

Transport 不再像 Go 1.10 和 Go 1.11 那样严格处理 HTTP/2 服务器 advertised 的 MAX_CONCURRENT_STREAMS 值。默认行为现在恢复到 Go 1.9 的方式:与服务器的每个连接最多可以有 MAX_CONCURRENT_STREAMS 个活动请求,然后根据需要创建新的 TCP 连接。在 Go 1.10 和 Go 1.11 中,http2 包会阻塞并等待请求完成,而不是创建新连接。要恢复更严格的行为,请直接导入 golang.org/x/net/http2 包并设置 Transport.StrictMaxConcurrentStreamstrue

net/url

ParseParseRequestURIURL.Parse 现在对包含 ASCII 控制字符(包括 NULL、制表符和换行符)的 URL 返回错误。

net/http/httputil

新的 ReverseProxy 现在会自动代理 WebSocket 请求。

os

新的 ProcessState.ExitCode 方法返回进程的退出代码。

ModeCharDevice 已添加到 ModeType 位掩码中,允许在用 ModeType 掩码 FileMode 时恢复 ModeDevice | ModeCharDevice

新函数 UserHomeDir 返回当前用户的主目录。

RemoveAll 现在支持大多数 Unix 系统上长度超过 4096 个字符的路径。

File.Sync 现在在 macOS 上使用 F_FULLFSYNC 来正确地将文件内容刷新到永久存储。这可能会导致该方法运行速度比以前的版本慢。

File 现在支持 SyscallConn 方法,该方法返回 syscall.RawConn 接口值。这可用于对底层文件描述符调用系统特定操作。

path/filepath

IsAbs 函数现在在传递 Windows 上的保留文件名(例如 NUL)时返回 true。保留名称列表。

reflect

新的 MapIter 类型是用于遍历映射的迭代器。此类型通过 Value 类型的新 MapRange 方法公开。它遵循与 range 语句相同的迭代语义,使用 Next 前进迭代器,以及 Key/Value 访问每个条目。

regexp

Copy 不再是避免锁竞争所必需的,因此它被部分弃用注释。如果使用 Copy 的原因是制作具有不同 Longest 设置的两个副本,那么它仍然可能是合适的。

runtime/debug

新的 BuildInfo 类型公开了从正在运行的二进制文件读取的构建信息,仅适用于使用模块支持构建的二进制文件。这包括主包路径、主模块信息和模块依赖项。此类型通过 BuildInfo 上的 ReadBuildInfo 函数提供。

strings

新函数 ReplaceAll 返回一个字符串的副本,其中所有非重叠的值实例都被另一个值替换。

零值 Reader 的指针现在在功能上等同于 NewReader(nil)。在 Go 1.12 之前,前者在所有情况下都不能替代后者。

新的 Builder.Cap 方法返回构建器底层字节切片的容量。

字符映射函数 MapTitleToLowerToLowerSpecialToTitleToTitleSpecialToUpperToUpperSpecial 现在始终保证返回有效的 UTF-8。在早期版本中,如果输入是无效的 UTF-8 但无需应用字符替换,这些例程会错误地返回未修改的无效 UTF-8。

syscall

FreeBSD 12 现在支持 64 位 inode。一些类型已相应调整。

现在支持兼容版本的 Windows 上的 Unix 套接字(AF_UNIX)地址族。

Windows 上引入了新函数 Syscall18,允许最多 18 个参数的调用。

syscall/js

Callback 类型和 NewCallback 函数已重命名;它们现在分别称为 FuncFuncOf。这是一个破坏性更改,但 WebAssembly 支持仍处于实验阶段,尚未受 Go 1 兼容性承诺的约束。任何使用旧名称的代码都需要更新。

如果某个类型实现了新的 Wrapper 接口,ValueOf 将使用它返回该类型的 JavaScript 值。

Value 的含义已更改。它现在表示 JavaScript 的 undefined 值,而不是数字零。这是一个破坏性更改,但 WebAssembly 支持仍处于实验阶段,尚未受 Go 1 兼容性承诺的约束。任何依赖零 Value 意味着数字零的代码都需要更新。

新的 Value.Truthy 方法报告给定值的 JavaScript“真值”

testing

-benchtime 标志现在支持设置显式迭代计数,而不是时间,当值以“x”结尾时。例如,-benchtime=100x 运行基准测试 100 次。

text/template

执行模板时,错误中不再截断长上下文值。

正在执行 "tmpl" at <.very.deep.context.v...>: 映射中没有 "notpresent" 的条目

现在是

正在执行 "tmpl" at <.very.deep.context.value.notpresent>: 映射中没有 "notpresent" 的条目

如果模板调用的用户定义函数发生 panic,该 panic 现在会被 ExecuteExecuteTemplate 方法捕获并作为错误返回。

time

$GOROOT/lib/time/zoneinfo.zip 中的时区数据库已更新到版本 2018i。请注意,此 ZIP 文件仅在操作系统未提供时区数据库时使用。

unsafe

将 nil unsafe.Pointer 转换为 uintptr 并通过算术转换回来是无效的。(这已经无效,但现在会导致编译器行为异常。)