Go 1.6 发布说明

Go 1.6 简介

最新的 Go 版本 1.6 在 1.5 发布六个月后到来。其大部分更改都集中在语言、运行时和库的实现上。语言规范没有变化。一如既往,此版本保持了 Go 1 兼容性承诺。我们预计几乎所有 Go 程序都将像以前一样继续编译和运行。

此版本新增了对 64 位 MIPS 上的 Linux 和 32 位 x86 上的 Android 的支持;定义并强制执行了 与 C 共享 Go 指针的规则;透明、自动的 HTTP/2 支持;以及一种新的 模板重用机制

语言变化

此版本没有语言更改。

移植

Go 1.6 新增了对 64 位 MIPS 上的 Linux (linux/mips64linux/mips64le) 的实验性支持。这些端口支持 cgo,但仅限于内部链接。

Go 1.6 还新增了对 32 位 x86 上的 Android (android/386) 的实验性支持。

在 FreeBSD 上,Go 1.6 默认使用 clang 而不是 gcc 作为外部 C 编译器。

在小端序 64 位 PowerPC 上的 Linux (linux/ppc64le) 上,Go 1.6 现在支持外部链接的 cgo,并且功能大致完整。

在 NaCl 上,Go 1.5 需要 SDK 版本 pepper-41。Go 1.6 新增了对更高 SDK 版本的支持。

在 32 位 x86 系统上,使用 -dynlink-shared 编译模式时,寄存器 CX 现在会被某些内存引用覆盖,应避免在手写的汇编代码中使用。有关详细信息,请参阅 汇编文档

工具

Cgo

cgo 有一个主要更改,以及一个次要更改。

主要更改是定义了与 C 代码共享 Go 指针的规则,以确保此类 C 代码可以与 Go 的垃圾回收器共存。简而言之,Go 和 C 可以在作为 cgo 调用的一部分将指向 Go 分配内存的指针传递给 C 时共享 Go 分配的内存,前提是内存本身不包含指向 Go 分配内存的指针,并且 C 在调用返回后不保留该指针。这些规则在程序执行期间由运行时检查:如果运行时检测到违规,它会打印诊断信息并使程序崩溃。这些检查可以通过设置环境变量 GODEBUG=cgocheck=0 来禁用,但请注意,绝大多数被检查识别出的代码都以这样或那样的方式与垃圾回收微妙地不兼容。禁用检查通常只会导致更神秘的故障模式。强烈建议修复相关代码,而不是关闭检查。有关详细信息,请参阅 cgo 文档

次要更改是新增了显式 C.complexfloatC.complexdouble 类型,与 Go 的 complex64complex128 不同。与其他数值类型一样,C 的复数类型和 Go 的复数类型不再可以互换。

编译器工具链

编译器工具链基本保持不变。在内部,最重要的变化是解析器现在是手写的,而不是从 yacc 生成的。

编译器、链接器和 go 命令新增了一个 -msan 标志,类似于 -race,并且只在 linux/amd64 上可用,它支持与 Clang MemorySanitizer 的交互。这种交互主要用于测试包含可疑 C 或 C++ 代码的程序。

链接器新增了一个 -libgcc 选项,用于在链接 cgo 代码时设置 C 编译器支持库的预期位置。该选项仅在使用 -linkmode=internal 时才会被考虑,并且可以设置为 none 以禁用支持库的使用。

Go 1.5 中开始的构建模式实现 已扩展到更多系统。此版本新增了对 android/386android/amd64android/arm64linux/386linux/arm64 上的 c-shared 模式的支持;对 linux/386linux/armlinux/amd64linux/ppc64le 上的 shared 模式的支持;以及对 android/386android/amd64android/armandroid/arm64linux/386linux/amd64linux/armlinux/arm64linux/ppc64le 上的新 pie 模式(生成位置无关的可执行文件)的支持。有关详细信息,请参阅 设计文档

提醒一下,链接器的 -X 标志在 Go 1.5 中发生了变化。在 Go 1.4 及更早版本中,它接受两个参数,例如:

-X importpath.name value

Go 1.5 增加了一种替代语法,使用一个参数,该参数本身是一个 name=value

-X importpath.name=value

在 Go 1.5 中,旧语法仍然被接受,但会打印警告,建议使用新语法。Go 1.6 继续接受旧语法并打印警告。Go 1.7 将取消对旧语法的支持。

Gccgo

GCC 和 Go 项目的发布计划不一致。GCC 5 版本包含 Go 1.4 版本的 gccgo。下一个版本 GCC 6 将包含 Go 1.6.1 版本的 gccgo。

Go 命令

go 命令的基本操作没有改变,但有许多值得注意的更改。

Go 1.5 引入了实验性的 vendoring 支持,通过将 GO15VENDOREXPERIMENT 环境变量设置为 1 启用。Go 1.6 保留了 vendoring 支持,不再认为是实验性的,并默认启用。可以通过将 GO15VENDOREXPERIMENT 环境变量设置为 0 来显式禁用。Go 1.7 将取消对该环境变量的支持。

默认启用 vendoring 最可能导致的问题发生在包含现有名为 vendor 目录的源代码树中,该目录不希望根据新的 vendoring 语义进行解释。在这种情况下,最简单的修复方法是将目录重命名为 vendor 以外的任何名称,并更新任何受影响的导入路径。

有关 vendoring 的详细信息,请参阅 go 命令设计文档 的文档。

新增了一个构建标志 -msan,它编译 Go 以支持 LLVM 内存清理器。这主要用于链接到正在使用内存清理器检查的 C 或 C++ 代码时。

Go doc 命令

Go 1.5 引入了 go doc 命令,它允许仅使用包名引用包,例如 go doc http。在 Go 1.5 中,如果存在歧义,则使用词典顺序最早的导入路径的包。在 Go 1.6 中,通过优先选择元素较少的导入路径来解决歧义,并使用词典比较来打破平局。这一变化的一个重要影响是现在优先选择包的原始副本而不是 vendored 副本。成功的搜索也往往运行得更快。

Go vet 命令

go vet 命令现在会诊断将函数或方法值作为参数传递给 Printf 的情况,例如在预期 f() 的地方传递 f

性能

一如既往,这些变化非常普遍和多样,因此很难对性能做出精确的说明。有些程序可能会运行得更快,有些则会更慢。平均而言,Go 1 基准测试套件中的程序在 Go 1.6 中比在 Go 1.5 中运行速度快百分之几。垃圾回收器的暂停时间甚至比 Go 1.5 更短,特别是对于使用大量内存的程序。

compress/bzip2compress/gzipcrypto/aescrypto/ellipticcrypto/ecdsasort 包的实现方面,已进行了重大优化,带来了超过 10% 的改进。

标准库

HTTP/2

Go 1.6 在 net/http 包中增加了对新 HTTP/2 协议 的透明支持。Go 客户端和服务器在使用 HTTPS 时将自动酌情使用 HTTP/2。没有针对 HTTP/2 协议处理细节的导出 API,就像没有针对 HTTP/1.1 的导出 API 一样。

必须禁用 HTTP/2 的程序可以通过将 Transport.TLSNextProto(对于客户端)或 Server.TLSNextProto(对于服务器)设置为非空、空映射来禁用。

需要调整 HTTP/2 协议特定细节的程序可以导入并使用 golang.org/x/net/http2,特别是其 ConfigureServerConfigureTransport 函数。

运行时

运行时已添加了对并发滥用映射的轻量级、尽力检测。一如既往,如果一个 goroutine 正在写入映射,则其他 goroutine 不应并发读取或写入映射。如果运行时检测到此情况,它将打印诊断信息并使程序崩溃。要了解更多关于此问题的信息,最好的方法是在 竞争检测器 下运行程序,它将更可靠地识别竞争并提供更多详细信息。

对于导致程序终止的 panic,运行时现在默认只打印正在运行的 goroutine 的堆栈,而不是所有现有 goroutine。通常只有当前 goroutine 与 panic 相关,因此省略其他 goroutine 显著减少了崩溃消息中无关的输出。要在崩溃消息中查看所有 goroutine 的堆栈,请将环境变量 GOTRACEBACK 设置为 all 或在崩溃前调用 debug.SetTraceback,然后重新运行程序。有关详细信息,请参阅 运行时文档

更新:旨在转储整个程序状态的未捕获 panic(例如检测到超时或显式处理接收到的信号时)现在应在 panic 之前调用 debug.SetTraceback("all")。搜索 signal.Notify 的用法可能有助于识别此类代码。

在 Windows 上,Go 1.5 及更早版本中的 Go 程序在启动时通过调用 timeBeginPeriod(1) 强制将全局 Windows 计时器分辨率设置为 1ms。Go 不再需要此功能来获得良好的调度器性能,并且更改全局计时器分辨率在某些系统上导致了问题,因此已删除了该调用。

使用 -buildmode=c-archive-buildmode=c-shared 构建存档或共享库时,信号处理已更改。在 Go 1.5 中,存档或共享库会为大多数信号安装信号处理程序。在 Go 1.6 中,它将只为处理 Go 代码中的运行时 panic 所需的同步信号安装信号处理程序:SIGBUS、SIGFPE、SIGSEGV。有关详细信息,请参阅 os/signal 包。

反射

reflect 包已 解决了 gc 和 gccgo 工具链之间关于包含导出字段的嵌入式未导出结构类型长期存在的不兼容性。使用反射遍历数据结构的代码,特别是实现 encoding/jsonencoding/xml 包风格的序列化的代码,可能需要更新。

当使用反射遍历嵌入式未导出结构类型字段到该结构的导出字段时,会出现问题。在这种情况下,reflect 曾错误地将嵌入式字段报告为已导出,通过返回空 Field.PkgPath。现在它正确地将字段报告为未导出,但在评估对结构中包含的导出字段的访问时忽略了该事实。

更新:通常,以前遍历结构并使用

f.PkgPath != ""

排除不可访问字段的代码现在应该使用

f.PkgPath != "" && !f.Anonymous

例如,请参阅 encoding/jsonencoding/xml 实现的更改。

排序

sort 包中,Sort 的实现已重写,使对 InterfaceLessSwap 方法的调用减少了大约 10%,从而节省了总体时间。新算法对于比较相等的值(即 Less(i, j)Less(j, i) 都为 false 的那些对)确实选择了与以前不同的排序。

更新Sort 的定义不保证相等值的最终顺序,但新行为仍可能破坏预期特定顺序的程序。此类程序应完善其 Less 实现以报告所需顺序,或切换到 Stable,后者保留相等值的原始输入顺序。

模板

text/template 包中,有两个重要的新功能可以使编写模板更容易。

首先,现在可以 修剪模板操作周围的空格,这可以使模板定义更具可读性。操作开头的减号表示修剪操作之前的空格,操作结尾的减号表示修剪操作之后的空格。例如,模板

{{23 -}}
   <
{{- 45}}

格式化为 23<45

其次,新的 {{block}} 操作,结合允许重新定义命名模板,提供了一种定义模板片段的简单方法,这些片段可以在不同的实例化中替换。在 text/template 包中有一个 示例 演示了这一新功能。

对库的微小更改

  • archive/tar 包的实现纠正了文件格式在罕见边缘情况下的许多错误。一个可见的变化是,Reader 类型的 Read 方法现在将特殊文件类型的内容显示为空,立即返回 io.EOF
  • archive/zip 包中,Reader 类型现在有一个 RegisterDecompressor 方法,而 Writer 类型现在有一个 RegisterCompressor 方法,从而可以控制单个 zip 文件的压缩选项。这些方法优先于已有的全局 RegisterDecompressorRegisterCompressor 函数。
  • bufio 包的 Scanner 类型现在有一个 Buffer 方法,用于指定在扫描过程中使用的初始缓冲区和最大缓冲区大小。这使得在需要时可以扫描大于 MaxScanTokenSize 的标记。同样对于 Scanner,该包现在定义了 ErrFinalToken 错误值,供 拆分函数 使用以中止处理或返回最终的空标记。
  • compress/flate 包已弃用其 ReadErrorcompress/flatecompress/gzipcompress/zlib 包现在对截断的输入流报告 io.ErrUnexpectedEOF,而不是 io.EOF
  • 在 GCM 解密失败的情况下,crypto/cipher 包现在会覆盖目标缓冲区。这是为了让 AESNI 代码避免使用临时缓冲区。
  • crypto/tls 包有一些小的改动。现在它允许当 ConfigCertificates 为 nil 时 Listen 成功,只要设置了 GetCertificate 回调;它增加了对 RSA 与 AES-GCM 密码套件的支持;它还增加了一个 RecordHeaderError,允许客户端(特别是 net/http 包)在尝试与非 TLS 服务器建立 TLS 连接时报告更好的错误。
  • crypto/x509 包现在允许证书包含负序列号(技术上是错误,但实际上很常见),并且它定义了一个新的 InsecureAlgorithmError,以便在拒绝使用 MD5 等不安全算法签名的证书时提供更好的错误消息。
  • debug/dwarfdebug/elf 包共同增加了对压缩 DWARF 段的支持。用户代码无需更新:读取时会自动解压缩这些段。
  • debug/elf 包增加了对一般压缩 ELF 段的支持。用户代码无需更新:读取时会自动解压缩这些段。但是,压缩的 Sections 不支持随机访问:它们具有 nil ReaderAt 字段。
  • encoding/asn1 包现在导出了 标签和类常量,这些常量对于高级解析 ASN.1 结构很有用。
  • 同样在 encoding/asn1 包中,Unmarshal 现在拒绝各种非标准的整数和长度编码。
  • encoding/base64 包的 Decoder 已修复,可以处理输入的最后字节。以前它尽可能多地处理四字节标记,但忽略了剩余的部分,最多三个字节。因此,Decoder 现在正确处理未填充编码(如 RawURLEncoding)中的输入,但它也拒绝填充编码中截断或以无效字节(如尾随空格)结尾的输入。
  • encoding/json 包现在在 marshaling Number 之前检查其语法,要求它符合 JSON 规范中的数值。与以前的版本一样,零 Number(空字符串)被 marshaling 为字面量 0(零)。
  • encoding/xml 包的 Marshal 函数现在支持 cdata 属性,例如 chardata,但将其参数编码在一个或多个 <![CDATA[ ... ]]> 标签中。
  • 同样在 encoding/xml 包中,DecoderToken 方法在遇到所有开放标签未关闭的 EOF 时现在会报告错误,这与其输入中标签必须正确匹配的一般要求一致。要避免该要求,请使用 RawToken
  • fmt 包现在允许任何整数类型作为 Printf* 宽度和精度规范的参数。在以前的版本中,* 的参数必须是 int 类型。
  • 同样在 fmt 包中,Scanf 现在可以使用 %X 扫描十六进制字符串,作为 %x 的别名。两种格式都接受任何大小写混合的十六进制。
  • imageimage/color 包添加了 NYCbCrANYCbCrA 类型,以支持具有非预乘 Alpha 的 Y’CbCr 图像。
  • io 包的 MultiWriter 实现现在实现了 WriteString 方法,供 WriteString 使用。
  • math/big 包中,Int 添加了 AppendText 方法,以提供更多打印控制。
  • 同样在 math/big 包中,Float 现在实现了 encoding.TextMarshalerencoding.TextUnmarshaler,允许它以自然形式通过 encoding/jsonencoding/xml 包进行序列化。
  • 同样在 math/big 包中,FloatAppend 方法现在支持特殊的精度参数 -1。正如在 strconv.ParseFloat 中一样,精度 -1 表示使用最少的位数,以便 Parse 将结果读入相同精度的 Float 时将产生原始值。
  • math/rand 包新增了 Read 函数,同样 Rand 也新增了 Read 方法。这些使得生成伪随机测试数据更加容易。请注意,与该包的其余部分一样,这些不应在加密环境中使用;为此目的,请改用 crypto/rand 包。
  • net 包的 ParseMAC 函数现在接受 20 字节的 IP-over-InfiniBand (IPoIB) 链路层地址。
  • 同样在 net 包中,DNS 查找有一些变化。首先,DNSError 错误实现现在实现了 Error,特别是其新的 IsTemporary 方法对于 DNS 服务器错误返回 true。其次,DNS 查找函数(例如 LookupAddr)现在在 Plan 9 和 Windows 上返回带根域名(带尾随点)的域名,以匹配 Go 在 Unix 系统上的行为。
  • net/http 包除了已经讨论过的 HTTP/2 支持之外,还有一些小的补充。首先,FileServer 现在按文件名对其生成的目录列表进行排序。其次,如果请求的 URL 路径包含“..” (点点) 作为路径元素,ServeFile 函数现在拒绝提供结果。程序通常应该使用 FileServerDir,而不是直接调用 ServeFile。需要响应包含点点 URL 的请求提供文件内容的程序仍然可以调用 ServeContent。第三,Client 现在允许用户代码设置 Expect: 100-continue 头(参见 Transport.ExpectContinueTimeout)。第四,有 五个新的错误代码StatusPreconditionRequired (428)、StatusTooManyRequests (429)、StatusRequestHeaderFieldsTooLarge (431) 和 StatusNetworkAuthenticationRequired (511) 来自 RFC 6585,以及最近批准的 StatusUnavailableForLegalReasons (451)。第五,CloseNotifier 的实现和文档已大幅更改。Hijacker 接口现在在以前使用 CloseNotifier 的连接上也能正常工作。文档现在描述了 CloseNotifier 何时应该工作。
  • 同样在 net/http 包中,关于处理其 Method 字段设置为空字符串的 Request 数据结构有一些更改。空的 Method 字段一直被记录为 "GET" 的别名,并且仍然如此。但是,Go 1.6 修复了一些例程,这些例程没有将空的 Method 与显式的 "GET" 同等对待。最值得注意的是,在以前的版本中,Client 仅在 Method 显式设置为 "GET" 时才遵循重定向;在 Go 1.6 中,Client 也遵循空 Method 的重定向。最后,NewRequest 接受一个 method 参数,该参数未被记录为允许为空。在过去的版本中,传递空的 method 参数会导致 Request 具有空的 Method 字段。在 Go 1.6 中,生成的 Request 始终具有初始化的 Method 字段:如果其参数为空字符串,NewRequest 会将返回的 Request 中的 Method 字段设置为 "GET"
  • net/http/httptest 包的 ResponseRecorder 现在使用与 http.Server 中相同的内容嗅探算法初始化默认的 Content-Type 头。
  • net/url 包的 Parse 在解析主机名方面现在更加严格并更符合规范。例如,主机名中的空格不再被接受。
  • 同样在 net/url 包中,Error 类型现在实现了 net.Error
  • os 包的 IsExistIsNotExistIsPermission 在查询 SyscallError 时现在返回正确的结果。
  • 在类 Unix 系统上,当写入 os.Stdoutos.Stderr(更准确地说,是为文件描述符 1 或 2 打开的 os.File)由于管道损坏错误而失败时,程序将引发 SIGPIPE 信号。默认情况下,这将导致程序退出;这可以通过为 syscall.SIGPIPE 调用 os/signal Notify 函数来更改。对除 1 或 2 之外的文件描述符上的损坏管道的写入将简单地向调用者返回 syscall.EPIPE(可能包装在 os.PathError 和/或 os.SyscallError 中)。在连续 10 次写入损坏管道后引发不可捕获的 SIGPIPE 信号的旧行为不再发生。
  • os/exec 包中,当命令以不成功的状态退出时,CmdOutput 方法继续返回 ExitError。如果标准错误本来会被丢弃,则返回的 ExitError 现在包含失败命令的标准错误输出的前缀和后缀(目前为 32 KB),用于调试或包含在错误消息中。ExitErrorString 方法不显示捕获的标准错误;程序必须从数据结构中单独检索它。
  • 在 Windows 上,path/filepath 包的 Join 函数现在正确处理基路径为相对驱动器路径的情况。例如,Join(c:, a) 现在返回 c:a,而不是像过去版本那样返回 c:\a。这可能会影响预期不正确结果的代码。
  • regexp 包中,Regexp 类型始终可以安全地被并发 goroutine 使用。它使用 sync.Mutex 来保护在正则表达式搜索期间使用的暂存空间缓存。一些高并发服务器在许多 goroutine 中使用相同的 Regexp 时,由于该互斥锁的争用而导致性能下降。为了帮助此类服务器,Regexp 现在有一个 Copy 方法,它会创建 Regexp 的副本,该副本共享原始结构的大部分,但拥有自己的暂存空间缓存。两个 goroutine 可以使用不同的 Regexp 副本而没有互斥锁争用。副本确实有额外的空间开销,因此只有在观察到争用时才应使用 Copy
  • strconv 包增加了 IsGraphic,类似于 IsPrint。它还增加了 QuoteToGraphicQuoteRuneToGraphicAppendQuoteToGraphicAppendQuoteRuneToGraphic,类似于 QuoteToASCIIQuoteRuneToASCII 等。ASCII 系列转义除 ASCII 空格 (U+0020) 之外的所有空格字符。相反,Graphic 系列不转义任何 Unicode 空格字符(Zs 类别)。
  • testing 包中,当测试调用 t.Parallel 时,该测试会暂停,直到所有非并行测试完成,然后该测试继续执行所有其他并行测试。Go 1.6 更改了此类测试的报告时间:以前的时间只计算并行执行,但现在它也计算从测试开始到调用 t.Parallel 的时间。
  • text/template 包除了上面描述的 主要更改 之外,还包含两个次要更改。首先,它添加了一个新的 ExecError 类型,用于在 Execute 期间发生的任何不源于对底层写入器进行 Write 的错误。调用者可以通过检查 ExecError 来区分模板使用错误和 I/O 错误。其次,Funcs 方法现在检查用作 FuncMap 中键的名称是否是可以在模板函数调用中出现的标识符。如果不是,Funcs 会 panic。
  • time 包的 Parse 函数一直拒绝任何大于 31 的月份日期,例如 1 月 32 日。在 Go 1.6 中,Parse 现在也拒绝非闰年的 2 月 29 日、2 月 30 日、2 月 31 日、4 月 31 日、6 月 31 日、9 月 31 日和 11 月 31 日。