Go 1.2 版本发布说明
Go 1.2 简介
自从 Go 1.1 版本 于 2013 年 4 月发布以来,发布周期已缩短,以提高发布流程的效率。此版本,即 Go 1.2 版本(简称 Go 1.2),大约在 1.1 版本发布六个月后发布,而 1.1 版本在 1.0 版本发布后则花费了一年多的时间才发布。由于时间周期缩短,1.2 版本的变化幅度小于从 1.0 到 1.1 版本的变化幅度,但它仍然包含一些重要的改进,包括一个更好的调度程序和一个新的语言特性。当然,Go 1.2 保持了 兼容性承诺。绝大多数使用 Go 1.1(或 1.0)构建的程序在迁移到 1.2 后无需任何更改即可运行,尽管对语言的一个角落引入了一个限制可能会暴露已存在的错误代码(请参阅关于 nil 的使用 的讨论)。
语言更改
为了明确规范,已经澄清了一个极端情况,这对程序有影响。此外,还增加了一个新的语言特性。
nil 的使用
现在语言规定,出于安全原因,某些 nil 指针的使用将保证触发运行时 panic。例如,在 Go 1.0 中,给定如下代码:
type T struct {
X [1<<24]byte
Field int32
}
func main() {
var x *T
...
}
nil
指针 x
可以被用来错误地访问内存:表达式 x.Field
可以访问地址 1<<24
处的内存。为了防止这种不安全的行为,在 Go 1.2 中,编译器现在保证任何通过 nil 指针进行的间接访问(如这里所示,也包括对数组的 nil 指针、nil 接口值、nil 切片等)要么 panic,要么返回一个正确、安全的非 nil 值。简而言之,任何显式或隐式需要计算 nil 地址的表达式都是错误的。实现可以在编译后的程序中注入额外的测试以强制执行此行为。
更多详细信息请参阅 设计文档。
更新:大多数依赖于旧行为的代码都是错误的,并在运行时失败。此类程序需要手动更新。
三索引切片
Go 1.2 添加了在对现有数组或切片使用切片操作时指定容量和长度的功能。切片操作通过描述已创建的数组或切片的一个连续部分来创建一个新的切片。
var array [10]int
slice := array[2:4]
切片的容量是切片可以容纳的最大元素数量,即使在重新切片后也是如此;它反映了底层数组的大小。在此示例中,slice
变量的容量为 8。
Go 1.2 添加了新的语法,允许切片操作指定容量和长度。第二个冒号引入容量值,该值必须小于或等于源切片或数组的容量,并根据起始位置进行调整。例如,
slice = array[2:4:7]
将切片的长度设置为与前面示例中相同,但其容量现在仅为 5 个元素(7-2)。无法使用此新切片值访问原始数组的最后三个元素。
在此三索引表示法中,缺少第一个索引([:i:j]
)默认为零,但其他两个索引必须始终显式指定。Go 的未来版本可能会为这些索引引入默认值。
更多详细信息请参阅 设计文档。
更新:这是一个向后兼容的更改,不影响任何现有程序。
实现和工具的更改
调度程序中的抢占
在之前的版本中,无限循环的 goroutine 可能会使同一线程上的其他 goroutine 饿死,当 GOMAXPROCS 只提供一个用户线程时,这是一个严重的问题。在 Go 1.2 中,这个问题得到了部分解决:调度程序在进入函数时偶尔会被调用。这意味着任何包含(非内联)函数调用的循环都可以被抢占,从而允许其他 goroutine 在同一线程上运行。
线程数量限制
Go 1.2 引入了对单个程序在其地址空间中可以拥有的线程总数的可配置限制(默认值为 10,000),以避免某些环境中的资源饥饿问题。请注意,goroutine 会多路复用到线程上,因此此限制不会直接限制 goroutine 的数量,而只会限制可能同时阻塞在系统调用中的数量。在实践中,很难达到此限制。
SetMaxThreads
函数(位于 runtime/debug
包中)控制线程计数限制。
更新:很少有函数会受到此限制的影响,但如果程序由于达到此限制而死亡,则可以修改它以调用 SetMaxThreads
来设置更高的计数。更好的方法是重构程序以减少线程需求,从而减少对内核资源的消耗。
栈大小
在 Go 1.2 中,创建 goroutine 时栈的最小大小已从 4KB 提高到 8KB。许多程序在旧大小下遇到了性能问题,旧大小往往会在性能关键部分引入代价高昂的栈段切换。新数字是通过经验测试确定的。
另一方面,SetMaxStack
函数(位于 runtime/debug
包中)控制单个 goroutine 栈的最大大小。64 位系统上的默认值为 1GB,32 位系统上的默认值为 250MB。在 Go 1.2 之前,失控的递归很容易消耗机器上的所有内存。
更新:增加的最小栈大小可能会导致具有许多 goroutine 的程序使用更多内存。没有解决方法,但未来版本的计划包括新的栈管理技术,应该可以更好地解决此问题。
Cgo 和 C++
cgo
命令现在将调用 C++ 编译器来构建链接到的库中用 C++ 编写的任何部分;文档 中提供了更多详细信息。
Godoc 和 vet 移至 go.tools 子存储库
这两个二进制文件仍然包含在发行版中,但 godoc 和 vet 命令的源代码已移至 go.tools 子存储库。
此外,godoc 程序的核心已被拆分为一个 库,而命令本身则位于一个单独的 目录 中。此移动允许轻松更新代码,并将代码分离为库和命令,从而更容易为本地站点和不同的部署方法构建自定义二进制文件。
更新:由于 godoc 和 vet 不是库的一部分,因此没有客户端 Go 代码依赖于它们的源代码,因此不需要更新。
从 golang.org 提供的二进制发行版包含这些二进制文件,因此使用这些发行版的用户不受影响。
从源代码构建时,用户必须使用“go get”安装 godoc 和 vet。(二进制文件将继续安装在其通常的位置,而不是 $GOPATH/bin
中。)
$ go get code.google.com/p/go.tools/cmd/godoc
$ go get code.google.com/p/go.tools/cmd/vet
gccgo 的状态
我们预计未来的 GCC 4.9 版本将包含对 Go 1.2 提供完全支持的 gccgo。在当前(4.8.2)版本的 GCC 中,gccgo 实现 Go 1.1.2。
gc 编译器和链接器的更改
Go 1.2 对 gc 编译器套件的工作原理进行了一些语义更改。大多数用户不会受到这些更改的影响。
cgo
命令现在可以在库中包含 C++ 时工作。有关详细信息,请参阅 cgo
文档。
当程序没有 package
子句时,gc 编译器会显示其起源的一个残留细节:它假设文件位于 package main
中。过去已被抹去,现在缺少 package
子句将是一个错误。
在 ARM 上,工具链支持“外部链接”,这是能够使用 gc 工具链构建共享库以及为需要动态链接的环境提供动态链接支持的一步。
在 ARM 的运行时中,使用 5a
时,可以使用 R9
和 R10
直接引用运行时内部的 m
(机器)和 g
(goroutine)变量。现在必须使用其正确的名称来引用它们。
同样在 ARM 上,5l
链接器(原文如此)现在将 MOVBS
和 MOVHS
指令定义为 MOVB
和 MOVH
的同义词,以更清楚地区分带符号和无符号的子字移动;无符号版本已经存在,并带有 U
后缀。
测试覆盖率
go test
的一个主要新特性是它现在可以计算并(在新的、单独安装的“go tool cover”程序的帮助下)显示测试覆盖率结果。
cover 工具是 go.tools
子存储库的一部分。可以通过运行以下命令安装它:
$ go get code.google.com/p/go.tools/cmd/cover
cover 工具执行两项操作。首先,当“go test”给出 -cover
标志时,它会自动运行以重写包的源代码并插入检测语句。然后,测试将像往常一样编译和运行,并报告基本的覆盖率统计信息。
$ go test -cover fmt
ok fmt 0.060s coverage: 91.4% of statements
$
其次,对于更详细的报告,“go test”的不同标志可以创建一个覆盖率配置文件,然后 cover 程序(使用“go tool cover”调用)可以分析该文件。
有关如何生成和分析覆盖率统计信息的详细信息,可以通过运行以下命令找到
$ go help testflag
$ go tool cover -help
go doc 命令已删除
“go doc” 命令已删除。请注意,godoc
工具本身并没有被删除,只是 go
命令对其的封装被删除了。它所做的只是根据包路径显示包的文档,而 godoc 本身已经能够以更高的灵活性做到这一点。因此,为了减少文档工具的数量,并且作为 godoc 重构的一部分,鼓励将来使用更好的选项,该命令已被删除。
更新:对于仍然需要运行以下命令的精确功能的用户
$ go doc
在目录中,其行为与运行以下命令相同
$ godoc .
go 命令的更改
go get
命令现在有一个 -t
标志,它会导致该命令下载包运行测试的依赖项,而不仅仅是包本身的依赖项。默认情况下,与之前一样,不会下载测试的依赖项。
性能
标准库中有一些重大的性能改进;以下是一些示例。
compress/bzip2
的解压缩速度提高了约 30%。crypto/des
包的速度提高了约 5 倍。encoding/json
包的编码速度提高了约 30%。- 通过在运行时使用集成的网络轮询器,Windows 和 BSD 系统上的网络性能提高了约 30%,这与 Go 1.1 中为 Linux 和 OS X 所做的类似。
标准库的更改
archive/tar 和 archive/zip 包
archive/tar
和 archive/zip
包对其语义进行了更改,这可能会破坏现有的程序。问题在于它们都提供了 os.FileInfo
接口的实现,但该实现不符合该接口的规范。特别是,它们的 Name
方法返回条目的完整路径名,但接口规范要求该方法仅返回基本名称(最后一个路径元素)。
更新:由于此行为是新实现的,并且有点模糊,因此可能没有代码依赖于此错误的行为。如果确实有程序依赖于此行为,则需要手动识别并修复它们。
新的 encoding 包
有一个新的包,encoding
,它定义了一组标准的编码接口,可用于为诸如 encoding/xml
、encoding/json
和 encoding/binary
等包构建自定义编组器和解编组器。这些新的接口已被用于整理标准库中的一些实现。
这些新接口称为 BinaryMarshaler
、BinaryUnmarshaler
、TextMarshaler
和 TextUnmarshaler
。完整详细信息请参阅该包的 文档 和单独的 设计文档。
fmt 包
fmt
包的格式化打印例程(例如 Printf
)现在允许通过在格式化规范中使用索引操作来以任意顺序访问要打印的数据项。无论何时要从参数列表中获取参数以进行格式化(作为要格式化的值或作为宽度或规范整数),新的可选索引表示法 [
n]
将获取参数 n。n 的值从 1 开始。在进行此类索引操作后,通过正常处理要获取的下一个参数将是 n+1。
例如,正常的 Printf
调用
fmt.Sprintf("%c %c %c\n", 'a', 'b', 'c')
将创建字符串 "a b c"
,但是使用以下索引操作
fmt.Sprintf("%[3]c %[1]c %c\n", 'a', 'b', 'c')
结果为“"c a b"
。[3]
索引访问第三个格式化参数,即 'c'
,[1]
访问第一个,'a'
,然后下一个获取访问其后的参数,'b'
。
此功能的动机是可编程格式语句,用于以不同的顺序访问参数以进行本地化,但它还有其他用途
log.Printf("trace: value %v of type %[1]T\n", expensiveFunction(a.b[c]))
更新:格式规范语法的更改严格向后兼容,因此它不会影响任何正在运行的程序。
text/template 和 html/template 包
text/template
包在 Go 1.2 中有几个更改,这两个更改也反映在 html/template
包中。
首先,有一些用于比较基本类型的新默认函数。这些函数列在以下表格中,该表格显示了它们的名字和相关的常用比较运算符。
名称 | 运算符 | |
---|---|---|
eq | == |
|
ne | != |
|
lt | < |
|
le | <= |
|
gt | > |
|
ge | >= |
这些函数的行为与相应的 Go 运算符略有不同。首先,它们仅对基本类型(bool
、int
、float64
、string
等)进行操作。(Go 还允许在某些情况下比较数组和结构体。)其次,只要值是相同类型的值,就可以比较它们:例如,任何带符号整数的值都可以与任何其他带符号整数的值进行比较。(Go 不允许比较 int8
和 int16
)。最后,eq
函数(仅)允许将第一个参数与一个或多个后续参数进行比较。此示例中的模板
{{if eq .A 1 2 3}} equal {{else}} not equal {{end}}
如果 .A
等于 1、2 或 3 中的任何一个,则报告“equal”。
第二个更改是语法的一小部分添加,使“if else if”链更容易编写。无需编写
{{if eq .A 1}} X {{else}} {{if eq .A 2}} Y {{end}} {{end}}
可以将第二个“if”折叠到“else”中,并且只有一个“end”,如下所示
{{if eq .A 1}} X {{else if eq .A 2}} Y {{end}}
这两种形式的效果相同;区别仅在于语法。
更新:“else if”更改和比较函数都不会影响现有程序。那些已经通过函数映射定义了名为 eq
等函数的程序不受影响,因为关联的函数映射将覆盖新的默认函数定义。
新的包
有两个新的包。
encoding
包在 上面进行了描述。image/color/palette
包提供标准颜色调色板。
库的次要更改
以下列表总结了库的一些次要更改,主要是添加。有关每个更改的更多信息,请参阅相关包文档。
archive/zip
包添加了DataOffset
访问器以返回文件(可能已压缩)数据在存档中的偏移量。bufio
包向Reader
和Writer
添加了Reset
方法。这些方法允许Readers
和Writers
在新的输入和输出读取器和写入器上重复使用,从而节省了分配开销。compress/bzip2
现在可以解压缩连接的存档。compress/flate
包在Writer
上添加了一个Reset
方法,以便在例如构建包含多个压缩文件的存档时减少分配。compress/gzip
包的Writer
类型添加了一个Reset
,以便可以重复使用它。compress/zlib
包的Writer
类型添加了一个Reset
,以便可以重复使用它。container/heap
包添加了一个Fix
方法,以提供更有效的方式更新项在堆中的位置。container/list
包添加了MoveBefore
和MoveAfter
方法,它们实现了明显的重排。crypto/cipher
包添加了新的 GCM 模式(Galois Counter Mode),该模式几乎总是与 AES 加密一起使用。crypto/md5
包添加了一个新的Sum
函数,以简化散列而不牺牲性能。- 类似地,
crypto/sha1
包添加了一个新的Sum
函数。 - 此外,
crypto/sha256
包添加了Sum256
和Sum224
函数。 - 最后,
crypto/sha512
包添加了Sum512
和Sum384
函数。 crypto/x509
包添加了对读取和写入任意扩展的支持。crypto/tls
包添加了对 TLS 1.1、1.2 和 AES-GCM 的支持。database/sql
包在DB
上添加了一个SetMaxOpenConns
方法,以限制到数据库的打开连接数。encoding/csv
包现在始终允许字段中存在尾随逗号。encoding/gob
包现在将结构体的通道和函数字段视为未导出的,即使它们不是未导出的也是如此。也就是说,它完全忽略它们。以前,它们会触发错误,如果嵌入式结构添加了此类字段,则可能会导致意外的兼容性问题。该包现在还支持上面描述的encoding
包的通用BinaryMarshaler
和BinaryUnmarshaler
接口。encoding/json
包现在始终会在打印字符串时将与号转义为“\u0026”。它现在将接受但在Marshal
中更正无效的 UTF-8(以前会拒绝此类输入)。最后,它现在支持上面描述的encoding
包的通用编码接口。encoding/xml
包现在允许编组存储在指针中的属性。它还通过新的Marshaler
、Unmarshaler
和相关的MarshalerAttr
和UnmarshalerAttr
接口支持上面描述的encoding
包的通用编码接口。该包还向Encoder
类型添加了一个Flush
方法,供自定义编码器使用。请参阅EncodeToken
的文档以了解如何使用它。flag
包现在有一个Getter
接口,允许检索标志的值。由于 Go 1 兼容性准则,此方法无法添加到现有的Value
接口中,但所有现有的标准标志类型都实现了它。该包现在还导出了CommandLine
标志集,该标志集保存来自命令行的标志。go/ast
包的SliceExpr
结构体有一个新的布尔字段Slice3
,当表示具有三个索引(两个冒号)的切片表达式时,该字段设置为 true。默认值为 false,表示通常的两个索引形式。go/build
包为Package
类型添加了AllTags
字段,以便更容易地处理构建标签。image/draw
包现在导出一个接口Drawer
,它封装了标准的Draw
方法。Porter-Duff 运算符现在实现了此接口,实际上将运算绑定到绘图运算符,而不是显式提供它。给定一个调色板图像作为其目标,新的FloydSteinberg
接口的实现将使用 Floyd-Steinberg 错误扩散算法绘制图像。为了创建适合这种处理的调色板,新的Quantizer
接口表示量化算法的实现,这些算法在给定全彩色图像的情况下选择调色板。库中没有此接口的实现。image/gif
包现在可以使用新的Encode
和EncodeAll
函数创建 GIF 文件。它们的 options 参数允许指定要使用的图像Quantizer
;如果它为nil
,则生成的 GIF 将使用新image/color/palette
包中定义的Plan9
颜色映射(调色板)。选项还指定要使用的Drawer
来创建输出图像;如果它为nil
,则使用 Floyd-Steinberg 错误扩散。io
包的Copy
方法现在以不同的优先级处理其参数。如果一个参数实现了WriterTo
而另一个参数实现了ReaderFrom
,则Copy
现在将调用WriterTo
来执行工作,以便通常需要更少的中间缓冲。net
包默认情况下需要 cgo,因为主机操作系统通常必须中介网络调用设置。但是,在某些系统上,可以在没有 cgo 的情况下使用网络,并且这样做很有用,例如为了避免动态链接。新的构建标签netgo
(默认情况下关闭)允许在那些可能的情况下在纯 Go 中构建net
包。net
包在Dialer
结构体中添加了一个新的字段DualStack
,用于使用双 IP 堆栈进行 TCP 连接设置,如 RFC 6555 中所述。net/http
包将不再传输根据 RFC 6265 不正确的 cookie。它只记录错误并发送空值。此外,net/http
包的ReadResponse
函数现在允许*Request
参数为nil
,此时它假设为 GET 请求。最后,HTTP 服务器现在将透明地服务 HEAD 请求,而无需在处理程序代码中进行特殊情况处理。在服务 HEAD 请求时,对Handler
的ResponseWriter
的写入将被Server
吸收,并且客户端将收到一个空主体,如 HTTP 规范要求的那样。os/exec
包的Cmd.StdinPipe
方法返回一个io.WriteCloser
,但已将其具体实现从*os.File
更改为一个嵌入*os.File
的未导出类型,并且现在可以安全地关闭返回的值。在 Go 1.2 之前,存在此更改修复的一个不可避免的竞争条件。需要访问*os.File
方法的代码可以使用接口类型断言,例如wc.(interface{ Sync() error })
。runtime
包放宽了SetFinalizer
中最终化函数的约束:实际参数现在可以是任何可以分配给函数形式类型的类型,就像 Go 中任何正常的函数调用一样。sort
包有一个新的Stable
函数,它实现了稳定排序。但是,它不如普通的排序算法高效。strings
包添加了一个IndexByte
函数,以与bytes
包保持一致。sync/atomic
包添加了一组新的交换函数,这些函数会原子地将参数与存储在指针中的值交换,并返回旧值。这些函数是SwapInt32
、SwapInt64
、SwapUint32
、SwapUint64
、SwapUintptr
和SwapPointer
,它交换一个unsafe.Pointer
。syscall
包现在为 Darwin 实现Sendfile
。testing
包现在导出TB
接口。它记录了T
和B
类型共有的方法,以便更容易地在测试和基准测试之间共享代码。此外,AllocsPerRun
函数现在将返回值量化为整数(尽管它仍然具有float64
类型),以舍入初始化引起的任何错误,并使结果更具可重复性。text/template
包现在在评估“转义”函数(如“html”)的参数时会自动取消引用指针值,以使此类函数的行为与其他打印函数(如“printf”)的行为保持一致。- 在
time
包中,Parse
函数和Format
方法现在处理带秒的时间区域偏移量,例如在历史日期“1871-01-01T05:33:02+00:34:08”中。此外,这些例程的格式中的模式匹配更加严格:现在必须在标准词语(如“Jan”和“Mon”)之后跟随一个非小写字母。 unicode
包添加了In
,它是原始IsOneOf
的一个更易于使用的但等效的版本,用于查看字符是否为 Unicode 类别的成员。