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/mips64
和 linux/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 版本的支持。
在使用 -dynlink
或 -shared
编译模式的 32 位 x86 系统上,寄存器 CX 现在会被某些内存引用覆盖,因此在手动编写的汇编代码中应避免使用它。有关详细信息,请参阅汇编文档。
工具
Cgo
对cgo
进行了重大更改,以及一项小幅更改。
重大更改是定义了与 C 代码共享 Go 指针的规则,以确保此类 C 代码可以与 Go 的垃圾回收器共存。简而言之,当将指向该内存的指针作为 cgo
调用的一部分传递给 C 时,Go 和 C 可以共享由 Go 分配的内存,前提是内存本身不包含指向 Go 分配的内存的指针,并且前提是 C 在调用返回后不会保留指针。这些规则在程序执行期间由运行时检查:如果运行时检测到违规,它会打印诊断信息并使程序崩溃。可以通过设置环境变量 GODEBUG=cgocheck=0
来禁用这些检查,但请注意,由这些检查识别的大部分代码以某种方式与垃圾回收器不兼容。禁用这些检查通常只会导致更加神秘的故障模式。修复相关代码应强烈优先于关闭检查。有关更多详细信息,请参阅cgo
文档。
小幅更改是添加了显式 C.complexfloat
和 C.complexdouble
类型,与 Go 的 complex64
和 complex128
分开。与其他数字类型匹配,C 的复数类型和 Go 的复数类型不再可以互换。
编译器工具链
编译器工具链基本保持不变。在内部,最重大的变化是解析器现在是用手工编写的,而不是从yacc生成的。
编译器、链接器和 go
命令都有一个新的标志 -msan
,类似于 -race
,并且仅在 linux/amd64 上可用,它启用与Clang 内存清理器的互操作性。这种互操作性主要用于测试包含可疑 C 或 C++ 代码的程序。
链接器有一个新的选项 -libgcc
,用于在链接cgo
代码时设置对 C 编译器支持库的预期位置。该选项仅在使用 -linkmode=internal
时才被考虑,并且可以设置为 none
以禁用使用支持库。
在 Go 1.5 中开始的构建模式的实现已扩展到更多系统。此版本为 android/386
、android/amd64
、android/arm64
、linux/386
和 linux/arm64
添加了对 c-shared
模式支持;为 linux/386
、linux/arm
、linux/amd64
和 linux/ppc64le
添加了对 shared
模式支持;并为 android/386
、android/amd64
、android/arm
、android/arm64
、linux/386
、linux/amd64
、linux/arm
、linux/arm64
和 linux/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.0 版本包含 Go 1.4 版本的 gccgo。下一个版本,GCC 6.0,将包含 Go 1.6.1 版本的 gccgo。
Go 命令
go
命令的基本操作保持不变,但有一些值得注意的更改。
Go 1.5 引入了对供应商的实验性支持,通过将 GO15VENDOREXPERIMENT
环境变量设置为 1
来启用。Go 1.6 保留了供应商支持,不再被认为是实验性的,并默认启用它。可以通过将 GO15VENDOREXPERIMENT
环境变量设置为 0
来显式禁用它。Go 1.7 将删除对该环境变量的支持。
默认启用供应商最有可能导致的问题发生在包含名为 vendor
的现有目录的源代码树中,该目录不希望根据新的供应商语义进行解释。在这种情况下,最简单的解决办法是将目录重命名为除 vendor
之外的任何名称,并更新所有受影响的导入路径。
有一个新的构建标志 -msan
,它使用对 LLVM 内存清理器支持来编译 Go。这主要用于在链接到使用内存清理器进行检查的 C 或 C++ 代码时使用。
Go doc 命令
Go 1.5 引入了go doc
命令,它允许使用仅包名的包引用,例如在 go
doc
http
中。在出现歧义的情况下,Go 1.5 的行为是使用词典序最早的导入路径的包。在 Go 1.6 中,通过优先考虑具有较少元素的导入路径来解决歧义,并使用词典序比较来打破平局。此更改的一个重要影响是,原始副本的包现在优先于供应商副本。成功的搜索通常也运行得更快。
Go vet 命令
go vet
命令现在诊断将函数或方法值作为参数传递给 Printf
的情况,例如在传递 f
的情况下,而本应传递 f()
。
性能
与以往一样,这些更改非常普遍且多样,因此难以对性能做出准确的陈述。有些程序可能运行得更快,有些可能运行得更慢。平均而言,Go 1 基准套件中的程序在 Go 1.6 中比在 Go 1.5 中运行快几个百分点。垃圾回收器的暂停时间比 Go 1.5 中的暂停时间更短,特别是对于使用大量内存的程序。
对compress/bzip2
、compress/gzip
、crypto/aes
、crypto/elliptic
、crypto/ecdsa
和 sort
软件包的实现进行了重大优化,带来了超过 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
,特别是其中的ConfigureServer 和ConfigureTransport 函数。
运行时
运行时添加了对并行错误使用映射的轻量级、尽力而为的检测。与以往一样,如果一个 goroutine 正在写入映射,则任何其他 goroutine 都不能同时读取或写入该映射。如果运行时检测到这种情况,它会打印诊断信息并使程序崩溃。了解问题的最佳方法是在竞争检测器下运行程序,它将更可靠地识别竞争并提供更多详细信息。
对于导致程序结束的 panic,运行时现在默认仅打印正在运行的 goroutine 的堆栈,而不是所有现有 goroutine 的堆栈。通常,只有当前 goroutine 与 panic 相关,因此省略其他 goroutine 会显着减少崩溃消息中无关输出的数量。要在崩溃消息中查看所有 goroutine 的堆栈,请将环境变量 GOTRACEBACK
设置为 all
或在崩溃前调用debug.SetTraceback
,然后重新运行程序。有关详细信息,请参阅运行时文档。
更新:旨在转储整个程序状态的未捕获恐慌,例如在检测到超时或显式处理接收到的信号时,现在应该在恐慌之前调用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 代码中运行时恐慌所需的同步信号安装信号处理程序:SIGBUS、SIGFPE、SIGSEGV。有关更多详细信息,请参阅 os/signal 包。
反射
reflect
包已 解决 gc 和 gccgo 工具链之间长期存在的关于包含导出字段的嵌入式未导出结构类型的不兼容性。使用反射遍历数据结构的代码,尤其是为了实现类似于 encoding/json
和 encoding/xml
包的序列化,可能需要更新。
当使用反射遍历嵌入式未导出结构类型字段进入该结构的导出字段时,就会出现此问题。在这种情况下,reflect
错误地报告了嵌入式字段为导出字段,通过返回一个空的Field.PkgPath
。现在它正确地报告了该字段为未导出字段,但在评估对结构内包含的导出字段的访问时会忽略这一事实。
更新:通常,以前遍历结构并使用
f.PkgPath != ""
来排除不可访问字段的代码现在应该使用
f.PkgPath != "" && !f.Anonymous
例如,请参阅对 encoding/json
和 encoding/xml
实现的更改。
排序
在 sort
包中,Sort
的实现已被重写,以减少约 10% 对 Interface
的Less
和Swap
方法的调用次数,并相应地节省了总体时间。新算法确实会为比较相等的值选择不同的排序顺序(对于Less(i,
j)
和Less(j,
i)
为假的那些对)。
更新: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 文件的压缩选项。这些优先于预先存在的全局RegisterDecompressor
和RegisterCompressor
函数。 bufio
包的Scanner
类型现在有一个Buffer
方法,用于指定在扫描过程中使用的初始缓冲区和最大缓冲区大小。这使得在需要时可以扫描大于MaxScanTokenSize
的标记。同样对于Scanner
,该包现在定义了ErrFinalToken
错误值,供 拆分函数 使用,以中止处理或返回最终的空标记。compress/flate
包已弃用其ReadError
和WriteError
错误实现。在 Go 1.5 中,它们只在遇到错误时很少返回;现在它们永远不会返回,尽管为了兼容性,它们仍然被定义。compress/flate
、compress/gzip
和compress/zlib
包现在为截断的输入流报告io.ErrUnexpectedEOF
,而不是io.EOF
。crypto/cipher
包现在在 GCM 解密失败的情况下覆盖目标缓冲区。这是为了允许 AESNI 代码避免使用临时缓冲区。crypto/tls
包有各种次要更改。它现在允许Listen
在Config
具有空Certificates
时成功,只要GetCertificate
回调已设置,它就会添加对 RSA 与 AES-GCM 密码套件的支持,并且它添加了一个RecordHeaderError
,以允许客户端(尤其是net/http
包)在尝试连接到非 TLS 服务器的 TLS 连接时报告更好的错误。crypto/x509
包现在允许证书包含负序列号(技术上是错误,但不幸的是在实践中很常见),它定义了一个新的InsecureAlgorithmError
,以便在拒绝使用不安全的算法(如 MD5)签署的证书时提供更好的错误消息。debug/dwarf
和debug/elf
包共同添加了对压缩 DWARF 部分的支持。用户代码不需要更新:部分在读取时会自动解压缩。debug/elf
包添加了对通用压缩 ELF 部分的支持。用户代码不需要更新:部分在读取时会自动解压缩。但是,压缩的Sections
不支持随机访问:它们有一个为 nil 的ReaderAt
字段。encoding/asn1
包现在导出 标签和类常量,这些常量对于高级解析 ASN.1 结构很有用。- 同样在
encoding/asn1
包中,Unmarshal
现在会拒绝各种非标准整数和长度编码。 encoding/base64
包的Decoder
已经修复,可以处理其输入的最后几个字节。之前,它处理尽可能多的四字节标记,但忽略了剩余部分,最多三个字节。因此,Decoder
现在可以正确处理未填充编码(如 RawURLEncoding)中的输入,但它也会拒绝填充编码中被截断或以无效字节结尾的输入,例如尾随空格。encoding/json
包现在会在编组之前检查Number
的语法,要求它符合 JSON 规范中的数值。与之前的版本一样,零Number
(空字符串)将被编组为文字 0(零)。encoding/xml
包的Marshal
函数现在支持cdata
属性,例如chardata
,但将它的参数编码在一个或多个<![CDATA[ ... ]]>
标签中。- 同样在
encoding/xml
包中,Decoder
的Token
方法现在会在遇到 EOF 之前看到所有打开的标签关闭时报告错误,这与其对输入中的标签必须正确匹配的一般要求一致。要避免这种要求,请使用RawToken
。 fmt
包现在允许任何整数类型作为Printf
的*
宽度和精度规范的参数。在之前的版本中,*
的参数必须是int
类型。- 同样在
fmt
包中,Scanf
现在可以使用 %X(作为 %x 的别名)扫描十六进制字符串。两种格式都接受任何大小写混合的十六进制。 image
和image/color
包添加了NYCbCrA
和NYCbCrA
类型,以支持具有非预乘 alpha 的 Y’CbCr 图像。io
包的MultiWriter
实现现在实现了一个WriteString
方法,供WriteString
使用。- 在
math/big
包中,Int
添加了Append
和Text
方法,以提供对打印的更多控制。 - 同样在
math/big
包中,Float
现在实现了encoding.TextMarshaler
和encoding.TextUnmarshaler
,允许它通过encoding/json
和encoding/xml
包以自然形式序列化。 - 同样在
math/big
包中,Float
的Append
方法现在支持特殊的精度参数 -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
现在按文件名对生成的目录列表进行排序。其次,ServeFile
函数现在拒绝为请求的 URL 路径包含 “..” (点-点)作为路径元素的服务结果。程序通常应该使用FileServer
和Dir
而不是直接调用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
包中,有一些与处理Request
数据结构有关的更改,其Method
字段设置为空字符串。空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
包的IsExist
,IsNotExist
和IsPermission
现在在询问SyscallError
时返回正确的结果。- 在类 Unix 系统上,当写入
os.Stdout
或os.Stderr
(更准确地说,是为文件描述符 1 或 2 打开的os.File
)由于管道破裂错误而失败时,程序将引发SIGPIPE
信号。默认情况下,这将导致程序退出;这可以通过为syscall.SIGPIPE
调用os/signal
Notify
函数来更改。写入文件描述符 1 或 2 以外的管道破裂将简单地返回syscall.EPIPE
(可能包装在os.PathError
和/或os.SyscallError
中)给调用者。写入管道破裂后连续 10 次写入时引发不可捕获SIGPIPE
信号的旧行为不再发生。 - 在
os/exec
包中,Cmd
的Output
方法继续在命令以不成功的状态退出时返回ExitError
。如果标准错误本来会被丢弃,则返回的ExitError
现在将包含失败命令的标准错误输出的前缀和后缀(当前为 32 KB),用于调试或包含在错误消息中。ExitError
的String
方法不会显示捕获的标准错误;程序必须单独从数据结构中检索它。 - 在 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
。它还添加了QuoteToGraphic
,QuoteRuneToGraphic
,AppendQuoteToGraphic
和AppendQuoteRuneToGraphic
,类似于QuoteToASCII
,QuoteRuneToASCII
等等。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
会恐慌。time
包的Parse
函数一直拒绝任何大于 31 的月份日期,例如 1 月 32 日。在 Go 1.6 中,Parse
现在还拒绝非闰年中的 2 月 29 日、2 月 30 日、2 月 31 日、4 月 31 日、6 月 31 日、9 月 31 日和 11 月 31 日。