Go 1.11 发布说明

Go 1.11 简介

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

语言变化

语言规范没有变化。

移植

正如 Go 1.10 发布说明中所宣布的,Go 1.11 现在需要 OpenBSD 6.2 或更高版本、macOS 10.10 Yosemite 或更高版本,或 Windows 7 或更高版本;对这些操作系统先前版本的支持已被移除。

Go 1.11 支持即将发布的 OpenBSD 6.4 版本。由于 OpenBSD 内核的变更,旧版本的 Go 将无法在 OpenBSD 6.4 上运行。

在 i386 硬件上的 NetBSD 存在已知问题

竞争检测器现在支持 linux/ppc64le,并在较小程度上支持 netbsd/amd64。NetBSD 竞争检测器的支持存在已知问题

内存消毒器 (-msan) 现在支持 linux/arm64

构建模式 c-sharedc-archive 现在支持 freebsd/amd64

在 64 位 MIPS 系统上,新的环境变量设置 GOMIPS64=hardfloat(默认)和 GOMIPS64=softfloat 用于选择浮点计算是使用硬件指令还是软件模拟。对于 32 位系统,环境变量仍为 GOMIPS,正如 Go 1.10 中所添加的

在软浮点 ARM 系统 (GOARM=5) 上,Go 现在使用更高效的软件浮点接口。这对 Go 代码是透明的,但使用未受 GOARM 保护的浮点指令的 ARM 汇编代码将会中断,必须移植到新接口

ARMv7 上的 Go 1.11 不再需要配置了 KUSER_HELPERS 的 Linux 内核。此设置在默认内核配置中启用,但在精简配置中有时会被禁用。

WebAssembly

Go 1.11 增加了一个实验性的 WebAssembly (js/wasm) 移植。

Go 程序目前编译成一个 WebAssembly 模块,该模块包含了用于 goroutine 调度、垃圾回收、map 等功能的 Go 运行时。因此,生成的文件大小至少约为 2 MB,压缩后为 500 KB。Go 程序可以使用新的实验性 syscall/js 包调用 JavaScript。二进制文件大小和与其他语言的互操作性尚未成为优先事项,但可能会在未来的版本中解决。

由于新增了 GOOS 值“js”和 GOARCH 值“wasm”,现在名为 *_js.go*_wasm.go 的 Go 文件将被 Go 工具忽略,除非正在使用这些 GOOS/GOARCH 值。如果您有符合这些模式的现有文件名,则需要重命名它们。

更多信息可以在 WebAssembly wiki 页面上找到。

RISC-V GOARCH 值已保留

主 Go 编译器尚不支持 RISC-V 架构但我们已经保留了 GOARCH 值“riscv”和“riscv64”,这些值由支持 RISC-V 的 Gccgo 使用。这意味着名为 *_riscv.go 的 Go 文件现在也将被 Go 工具忽略,除非正在使用这些 GOOS/GOARCH 值。

工具

模块、包版本控制和依赖管理

Go 1.11 初步支持一个名为“模块”的新概念,这是 GOPATH 的替代方案,集成了对版本控制和包分发的支持。使用模块,开发人员不再局限于 GOPATH 内工作,版本依赖信息明确且轻量,构建过程也更加可靠和可复现。

模块支持被认为是实验性的。细节可能会根据 Go 1.11 用户的反馈而改变,并且我们计划了更多的工具。尽管模块支持的细节可能会改变,但使用 Go 1.11 转换为模块的项目将继续与 Go 1.12 及更高版本兼容。如果您在使用模块时遇到错误,请提交问题,以便我们修复。更多信息,请参阅 go 命令文档

导入路径限制

由于 Go 模块支持在命令行操作中为 @ 符号赋予了特殊含义,go 命令现在禁止使用包含 @ 符号的导入路径。go get 从未允许过此类导入路径,因此此限制只会影响通过其他方式构建自定义 GOPATH 树的用户。

包加载

新包 golang.org/x/tools/go/packages 提供了一个简单的 API 用于定位和加载 Go 源代码包。虽然它还不是标准库的一部分,但在许多任务中,它有效地取代了 go/build 包,后者的 API 无法完全支持模块。因为它运行一个外部查询命令(如 go list)来获取 Go 包的信息,所以它使得构建的分析工具能够同样良好地与替代构建系统(如 BazelBuck)协同工作。

构建缓存要求

Go 1.11 将是最后一个支持设置环境变量 GOCACHE=off 来禁用 Go 1.10 中引入的构建缓存的版本。从 Go 1.12 开始,构建缓存将是必需的,这是为了逐步淘汰 $GOPATH/pkg。上述模块和包加载支持已经要求启用构建缓存。如果您因为遇到的问题而禁用了构建缓存,请提交问题让我们知道。

编译器工具链

现在默认有更多的函数符合内联条件,包括调用 panic 的函数。

编译器工具链现在支持行指令中的列信息。

引入了一种新的包导出数据格式。这对最终用户应该是透明的,除了可以加快大型 Go 项目的构建时间。如果它确实引起问题,可以通过在构建二进制文件时向 go 工具传递 -gcflags=all=-iexport=false 来再次关闭它。

编译器现在会拒绝在类型 switch 守卫中声明的未使用变量,例如下面例子中的 x

func f(v interface{}) {
    switch x := v.(type) {
    }
}

这已经被 gccgogo/types 拒绝。

汇编器

amd64 的汇编器现在接受 AVX512 指令。

调试

编译器现在为优化后的二进制文件生成更精确的调试信息,包括变量位置信息、行号和断点位置。这应该使得在没有使用 -N -l 编译的二进制文件上进行调试成为可能。调试信息的质量仍然存在限制,其中一些是根本性的,另一些将随着未来版本继续改进。

由于编译器生成了扩展且更准确的调试信息,DWARF 部分现在默认被压缩。这对大多数 ELF 工具(如 Linux 和 *BSD 上的调试器)是透明的,并且在所有平台上都得到 Delve 调试器的支持,但在 macOS 和 Windows 的原生工具中支持有限。要禁用 DWARF 压缩,请在构建二进制文件时向 go 工具传递 -ldflags=-compressdwarf=false

Go 1.11 增加了在调试器内调用 Go 函数的实验性支持。例如,这在断点暂停时调用 String 方法非常有用。目前这仅受 Delve(版本 1.1.0及以上)支持。

Test

自 Go 1.10 起,go test 命令会在被测试的包上运行 go vet,以便在运行测试前识别问题。由于 vet 在运行前使用 go/types 对代码进行类型检查,因此无法通过类型检查的测试现在会失败。特别是,在 Go 1.10 中编译的闭包中包含未使用变量的测试,之前 Go 编译器错误地接受了它们(问题 #3059),但现在会失败,因为 go/types 在这种情况下会正确报告“未使用变量”错误。

go test-memprofile 标志现在默认为“allocs”配置文件,它记录自测试开始以来分配的总字节数(包括被垃圾回收的字节)。

Vet

当被分析的包无法通过类型检查时,go vet 命令现在会报告一个致命错误。以前,类型检查错误只会打印一个警告,并使 vet 以状态码 1 退出。

此外,go vet 在格式检查 printf 包装器时变得更加健壮。Vet 现在能检测到这个例子中的错误

func wrapper(s string, args ...interface{}) {
    fmt.Printf(s, args...)
}

func main() {
    wrapper("%s", 42)
}

Trace

通过新的 runtime/trace 包的用户注解 API,用户可以在执行跟踪中记录应用级信息,并创建相关 goroutine 的组。go tool trace 命令在跟踪视图和新的用户任务/区域分析页面中将这些信息可视化。

Cgo

自 Go 1.10 起,cgo 已将一些 C 指针类型转换为 Go 的 uintptr 类型。这些类型包括 Darwin 的 CoreFoundation 框架中的 CFTypeRef 层次结构和 Java 的 JNI 接口中的 jobject 层次结构。在 Go 1.11 中,检测这些类型的代码已进行了一些改进。使用这些类型的代码可能需要一些更新。详情请参阅 Go 1.10 发布说明

Go 命令

环境变量 GOFLAGS 现在可用于为 go 命令设置默认标志。这在某些情况下很有用。由于 DWARF 的原因,链接在性能较差的系统上可能会明显变慢,用户可能希望默认设置 -ldflags=-w。对于模块,一些用户和 CI 系统总是希望使用 vendoring,所以他们应该默认设置 -mod=vendor。更多信息,请参阅 go 命令文档

Godoc

Go 1.11 将是最后一个支持 godoc 命令行界面的版本。在未来的版本中,godoc 将只作为 Web 服务器。用户应改用 go doc 来获取命令行帮助输出。

godoc Web 服务器现在显示引入新 API 功能的 Go 版本。类型、函数和方法的初始 Go 版本会右对齐显示。例如,请看 UserCacheDir,右侧有“1.11”。对于结构体字段,如果该字段是在与类型本身引入时不同的 Go 版本中添加的,则会添加内联注释。关于结构体字段的示例,请看 ClientTrace.Got1xxResponse

Gofmt

Go 源代码默认格式的一个小细节发生了变化。在格式化带有内联注释的表达式列表时,注释会根据一个启发式规则进行对齐。然而,在某些情况下,对齐很容易被破坏,或者引入过多的空白。这个启发式规则已经改变,以更好地适应人类编写的代码。

请注意,gofmt 的这类小更新是会不时发生的。通常,需要 Go 源代码格式一致的系统应使用特定版本的 gofmt 二进制文件。更多信息请参阅 go/format 包文档。

Run

go run 命令现在允许单个导入路径、一个目录名或一个匹配单个包的模式。这允许 go run pkggo run dir,最重要的是 go run .

运行时

运行时现在使用稀疏堆布局,因此不再有 Go 堆大小的限制(以前的限制是 512GiB)。这也修复了混合 Go/C 二进制文件或使用 -race 编译的二进制文件中罕见的“地址空间冲突”失败。

在 macOS 和 iOS 上,运行时现在使用 libSystem.dylib 而不是直接调用内核。这应该会使 Go 二进制文件与未来版本的 macOS 和 iOS 更兼容。syscall 包仍然进行直接的系统调用;计划在未来版本中修复此问题。

性能

与往常一样,这些变化是如此普遍和多样,以至于很难对性能做出精确的陈述。由于生成的代码更好以及核心库的优化,大多数程序应该运行得稍快一些。

math/big 包有多项性能改进,以及整个代码树中针对 GOARCH=arm64 的许多更改。

编译器工具链

编译器现在优化了以下形式的 map 清除操作

for k := range m {
    delete(m, k)
}

编译器现在优化了 append(s, make([]T, n)...) 形式的切片扩展。

编译器现在执行明显更积极的边界检查和分支消除。值得注意的是,它现在能识别传递关系,因此如果 i<jj<len(s),它可以利用这些事实来消除对 s[i] 的边界检查。它还理解简单的算术,如 s[i-10],并能识别循环中更多的归纳情况。此外,编译器现在使用边界信息来更积极地优化移位操作。

标准库

所有对标准库的更改都是微小的。

对库的微小更改

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

crypto

某些加密操作,包括 ecdsa.Signrsa.EncryptPKCS1v15rsa.GenerateKey,现在会随机读取一个额外的随机字节,以确保测试不依赖于内部行为。

crypto/cipher

新函数 NewGCMWithTagSize 实现了带有非标准标签长度的伽罗瓦/计数器模式(GCM),以兼容现有的加密系统。

crypto/rsa

PublicKey 现在实现了一个 Size 方法,该方法返回模数的大小(以字节为单位)。

crypto/tls

ConnectionState 的新方法 ExportKeyingMaterial 允许根据 RFC 5705 导出与连接绑定的密钥材料。

crypto/x509

当 `CommonName` 字段不是有效主机名时,在没有主题备用名称(SAN)的情况下将其视为 主机名的已弃用旧行为现已被禁用。通过将实验性值 `x509ignoreCN=1` 添加到 `GODEBUG` 环境变量中,可以完全忽略 `CommonName`。当 `CommonName` 被忽略时,没有 SAN 的证书在带有名称约束的链下会进行验证,而不是返回 `NameConstraintsWithoutSANs`。

扩展密钥用法限制现在仅在它们出现在 VerifyOptionsKeyUsages 字段中时才被检查,而不是总是被检查。这与 Go 1.9 及更早版本的行为相匹配。

SystemCertPool 返回的值现在被缓存,并且可能不会反映两次调用之间的系统更改。

debug/elf

添加了更多 ELFOSABIEM 常量。

encoding/asn1

MarshalUnmarshal 现在支持字段的“private”类注解。

encoding/base32

解码器现在对于不完整的块会一致地返回 io.ErrUnexpectedEOF。以前在某些情况下它会返回 io.EOF

encoding/csv

Reader 现在拒绝将 Comma 字段设置为双引号字符的尝试,因为双引号字符在 CSV 中已经有特殊含义。

html/template

当一个有类型的接口值被传递给隐式转义函数时,该包的行为已改变。以前,这样的值被写出为 <nil> 的(转义形式)。现在,这样的值被忽略,就像一个无类型的 nil 值被(并且一直)忽略一样。

image/gif

现在支持非循环的动画 GIF。它们通过将 LoopCount 设置为 -1 来表示。

io/ioutil

TempFile 函数现在支持指定文件名中随机字符的位置。如果 prefix 参数包含一个“*”,随机字符串将替换“*”。例如,一个 prefix 参数为“myname.*.bat”将产生一个随机文件名,如“myname.123456.bat”。如果不包含“*”,则保留旧的行为,随机数字会附加到末尾。

math/big

当 g 和 n 不互质时,ModInverse 现在返回 nil。之前的结果是未定义的。

mime/multipart

对缺少/空文件名的表单数据处理已恢复到 Go 1.9 的行为:在表单数据部分的 Form 中,该值在 Value 字段中可用,而不是在 File 字段中。在 Go 1.10 到 1.10.3 版本中,一个缺少/空文件名且“Content-Type”字段非空的表单数据部分被存储在 File 字段中。这个改动在 1.10 中是一个错误,现已恢复到 1.9 的行为。

mime/quotedprintable

为了支持在实际中发现的无效输入,该包现在允许非 ASCII 字节,但不对其编码进行验证。

net

新的 ListenConfig 类型和新的 Dialer.Control 字段分别允许在接受和创建连接之前设置套接字选项。

syscall.RawConnReadWrite 方法现在在 Windows 上可以正常工作。

在 Linux 上,net 包现在在 TCPConn.ReadFrom 中(由 io.Copy 调用)复制 TCP 连接之间的数据时,会自动使用 splice 系统调用。结果是 TCP 代理更快、更高效。

TCPConn.FileUDPConn.FileUnixConn.FileIPConn.File 方法不再将返回的 *os.File 设置为阻塞模式。

net/http

Transport 类型有了一个新的 MaxConnsPerHost 选项,允许限制每个主机的最大连接数。

Cookie 类型有了一个新的 SameSite 字段(新类型也名为 SameSite),用于表示最近大多数浏览器支持的新 cookie 属性。net/httpTransport 本身不使用 SameSite 属性,但该包支持解析和序列化该属性供浏览器使用。

在调用 ShutdownClose 后,不再允许重用一个 Server。过去这从未被正式支持,并且常常有令人意外的行为。现在,在关闭或关闭后,所有对服务器 Serve 方法的未来调用都将返回错误。

常量 StatusMisdirectedRequest 现在为 HTTP 状态码 421 定义。

HTTP 服务器在接收到管道化的 HTTP/1.1 请求时,将不再取消上下文或在 CloseNotifier 通道上发送信号。浏览器不使用 HTTP 管道,但某些客户端(例如 Debian 的 apt)可能配置为使用。

ProxyFromEnvironment,被 DefaultTransport 使用,现在在 NO_PROXY 环境变量中支持 CIDR 表示法和端口。

net/http/httputil

ReverseProxy 有一个新选项 ErrorHandler,允许更改错误处理方式。

ReverseProxy 现在也会将 “TE: trailers” 请求头传递给后端,这是 gRPC 协议所要求的。

os

新函数 UserCacheDir 返回用于用户特定缓存数据的默认根目录。

新的 ModeIrregular 是一个 FileMode 位,用于表示一个文件不是常规文件,但对其一无所知,或者它不是套接字、设备、命名管道、符号链接,或 Go 有定义模式位的其他文件类型。

在启用了开发者模式的 Windows 10 机器上,Symlink 现在对非特权用户也有效。

当一个非阻塞描述符传递给 NewFile 时,生成的 *File 将保持在非阻塞模式。这意味着该 *File 的 I/O 将使用运行时轮询器而不是单独的线程,并且 SetDeadline 方法将起作用。

os/signal

新函数 Ignored 报告一个信号当前是否被忽略。

os/user

os/user 包现在可以使用构建标签 “osusergo” 在纯 Go 模式下构建,这与是否使用环境变量 CGO_ENABLED=0 无关。以前,使用该包的纯 Go 实现的唯一方法是在整个程序中禁用 cgo 支持。

runtime

设置 GODEBUG=tracebackancestors=N 环境变量现在会用 goroutine 创建时的堆栈来扩展回溯信息,其中 N 限制了要报告的祖先 goroutine 的数量。

runtime/pprof

此版本增加了一个新的“allocs”配置文件类型,用于分析程序开始以来分配的总字节数(包括垃圾回收的字节)。这与在 -alloc_space 模式下查看的现有“heap”配置文件相同。现在 go test -memprofile=... 报告的是“allocs”配置文件而不是“heap”配置文件。

sync

互斥锁配置文件现在包括 RWMutex 的读/写竞争。写/写竞争已包含在互斥锁配置文件中。

syscall

在 Windows 上,有几个字段从 uintptr 更改为新的 Pointer 类型,以避免与 Go 的垃圾回收器产生问题。同样的变化也应用到了 golang.org/x/sys/windows 包。对于任何受影响的代码,用户应首先从 syscall 包迁移到 golang.org/x/sys/windows 包,然后改为使用 Pointer,同时遵守 unsafe.Pointer 转换规则

在 Linux 上,Faccessatflags 参数现在已实现,就像在 glibc 中一样。在早期的 Go 版本中,flags 参数被忽略了。

在 Linux 上,Fchmodatflags 参数现在会进行验证。Linux 的 fchmodat 不支持 flags 参数,所以我们现在模仿 glibc 的行为,如果它非零则返回一个错误。

text/scanner

Scanner.Scan 方法现在对原始字符串字面量返回 RawString 标记,而不是 String

text/template

现在允许通过 = 标记来修改模板变量

  {{ $v := "init" }}
  {{ if true }}
    {{ $v = "changed" }}
  {{ end }}
  v: {{ $v }} {{/* "changed" */}}

在以前的版本中,传递给模板函数的无类型 nil 值被忽略了。现在它们作为普通参数传递。

time

现在支持解析由符号和偏移量表示的时区。在以前的版本中,数字时区名称(如 +03)不被视为有效,只有三字母缩写(如 MST)在期望时区名称时被接受。