Go 1 版本说明

Go 1 简介

Go 版本 1(简称 Go 1)定义了一种语言和一组核心库,为创建可靠的产品、项目和出版物提供稳定的基础。

Go 1 的主要动力是为用户提供稳定性。人们应该能够编写 Go 程序,并期望它们能够在数年内持续编译和运行而无需更改,包括在 Google App Engine 等生产环境中。同样,人们应该能够编写关于 Go 的书籍,能够说明书籍描述的是哪个版本的 Go,并且该版本号在很久以后仍然具有意义。

在 Go 1 中编译的代码,除了少数例外,应该能够在该版本的整个生命周期内持续编译和运行,即使我们发布更新和错误修复,例如 Go 版本 1.1、1.2 等。除了关键修复之外,针对后续 Go 1 版本对语言和库所做的更改可能会添加功能,但不会破坏现有的 Go 1 程序。Go 1 兼容性文档更详细地解释了兼容性指南。

Go 1 代表了 Go 当前的使用方式,而不是对语言的彻底重新思考。我们避免设计新功能,而是专注于清理问题和不一致之处,并提高可移植性。我们曾考虑过并对 Go 语言和包进行了一些更改,并进行了原型设计,但主要是因为它们具有重大意义且向后不兼容而没有发布。Go 1 是一个发布这些更改的机会,这对长期发展很有帮助,但也意味着 Go 1 引入了与旧程序的不兼容性。幸运的是,go fix 工具可以自动化将程序更新到 Go 1 标准所需的大部分工作。

本文档概述了 Go 1 中将影响程序员更新现有代码的主要更改;其参考点是之前的版本 r60(标记为 r60.3)。它还解释了如何更新 r60 中的代码以便在 Go 1 下运行。

语言更改

追加

append 预声明的可变参数函数使通过在末尾添加元素来轻松扩展切片变得容易。一个常见的用法是在生成输出时将字节追加到字节切片的末尾。但是,append 没有提供将字符串追加到 []byte 的方法,这也是另一种常见情况。

    greeting := []byte{}
    greeting = append(greeting, []byte("hello ")...)

类似于 copy 的类似属性,Go 1 允许将字符串(逐字节)直接追加到字节切片,减少了字符串和字节切片之间的摩擦。转换不再是必需的。

    greeting = append(greeting, "world"...)

更新:这是一个新功能,因此现有代码不需要更改。

关闭

close 预声明函数为发送方提供了一种机制,用于发出不再发送值的信号。这对于实现 for range 循环遍历通道非常重要,并且在其他情况下也很有用。部分由于设计,部分由于可能发生的竞争条件,它旨在仅供在通道上发送的 goroutine 使用,而不是供接收数据的 goroutine 使用。但是,在 Go 1 之前,没有编译时检查 close 是否被正确使用。

为了至少部分地弥合这一差距,Go 1 不允许在仅接收通道上使用 close。尝试关闭此类通道将导致编译时错误。

    var c chan int
    var csend chan<- int = c
    var crecv <-chan int = c
    close(c)     // legal
    close(csend) // legal
    close(crecv) // illegal

更新:尝试关闭仅接收通道的现有代码即使在 Go 1 之前也是错误的,应该进行修复。编译器现在将拒绝此类代码。

复合字面量

在 Go 1 中,如果数组、切片或映射类型的复合字面量的元素初始化程序为指针类型,则可以省略元素初始化程序的类型规范。此示例中的所有四个初始化都是合法的;最后一个在 Go 1 之前是非法的。

    type Date struct {
        month string
        day   int
    }
    // Struct values, fully qualified; always legal.
    holiday1 := []Date{
        Date{"Feb", 14},
        Date{"Nov", 11},
        Date{"Dec", 25},
    }
    // Struct values, type name elided; always legal.
    holiday2 := []Date{
        {"Feb", 14},
        {"Nov", 11},
        {"Dec", 25},
    }
    // Pointers, fully qualified, always legal.
    holiday3 := []*Date{
        &Date{"Feb", 14},
        &Date{"Nov", 11},
        &Date{"Dec", 25},
    }
    // Pointers, type name elided; legal in Go 1.
    holiday4 := []*Date{
        {"Feb", 14},
        {"Nov", 11},
        {"Dec", 25},
    }

更新:此更改对现有代码没有影响,但应用于现有源代码的命令 gofmt -s 将(除其他外)在允许的情况下省略显式元素类型。

init 期间的 Goroutine

旧语言定义了在初始化期间执行的 go 语句创建了 goroutine,但它们直到整个程序的初始化完成才开始运行。这在许多地方引入了笨拙,并且实际上限制了 init 构造的实用性:如果另一个包有可能在初始化期间使用该库,则该库被迫避免使用 goroutine。这种设计是为了简单和安全的考虑,但随着我们对语言的信心增强,它似乎变得不必要了。在初始化期间运行 goroutine 与在正常执行期间运行它们一样不复杂或不安全。

在 Go 1 中,使用 goroutine 的代码可以从 init 例程和全局初始化表达式中调用,而不会引入死锁。

var PackageGlobal int

func init() {
    c := make(chan int)
    go initializationFunction(c)
    PackageGlobal = <-c
}

更新:这是一个新功能,因此现有代码不需要更改,尽管依赖于 goroutine 在 main 之前不启动的代码可能会中断。标准存储库中没有此类代码。

rune 类型

语言规范允许 int 类型为 32 位或 64 位宽,但当前实现即使在 64 位平台上也将 int 设置为 32 位。最好让 int 在 64 位平台上为 64 位。(这对索引大型切片有重要影响。)但是,此更改在使用旧语言处理 Unicode 字符时会浪费空间,因为 int 类型也用于保存 Unicode 代码点:如果 int 从 32 位增长到 64 位,则每个代码点将浪费额外的 32 位存储空间。

为了使更改为 64 位 int 可行,Go 1 引入了一种新的基本类型 rune 来表示单个 Unicode 代码点。它类似于 byte 作为 uint8 的别名,是 int32 的别名。

字符字面量(如 'a''語''\u0345')现在具有默认类型 rune,类似于 1.0 具有默认类型 float64。因此,初始化为字符常量的变量将具有类型 rune,除非另有指定。

库已更新为在适当情况下使用 rune 而不是 int。例如,函数 unicode.ToLower 及其相关函数现在接受并返回 rune

    delta := 'δ' // delta has type rune.
    var DELTA rune
    DELTA = unicode.ToUpper(delta)
    epsilon := unicode.ToLower(DELTA + 1)
    if epsilon != 'δ'+1 {
        log.Fatal("inconsistent casing for Greek")
    }

更新:大多数源代码都不会受到此影响,因为来自 := 初始化程序的类型推断会静默地引入新类型,并且会从那里传播。某些代码可能会出现可以通过简单的转换解决的类型错误。

error 类型

Go 1 引入了一种新的内置类型 error,其定义如下:

    type error interface {
        Error() string
    }

由于此类型的后果都在包库中,因此将在下面讨论。

从映射中删除

在旧语言中,要从映射 m 中删除键为 k 的条目,可以编写以下语句:

    m[k] = value, false

此语法是一个特殊的特殊情况,是唯一的二对一赋值。它需要传递一个(通常被忽略的)被评估但被丢弃的值,加上一个几乎总是常量 false 的布尔值。它完成了工作,但很奇怪并且存在争议。

在 Go 1 中,该语法已消失;取而代之的是一个新的内置函数 delete。调用

    delete(m, k)

将删除表达式 m[k] 检索到的映射条目。没有返回值。删除不存在的条目是一个无操作。

更新:运行 go fix 将表达式 m[k] = value, false 转换为 delete(m, k),当清楚地表明可以安全地从程序中丢弃被忽略的值并且 false 指的是预定义的布尔常量时。修复工具将标记语法的其他用法,以便程序员检查。

在映射中迭代

旧语言规范没有定义映射迭代的顺序,并且在实践中它在不同的硬件平台上有所不同。这导致迭代映射的测试变得脆弱且不可移植,并且具有令人不快的特性,即测试可能始终在一台机器上通过,但在另一台机器上失败。

在 Go 1 中,使用 for range 语句迭代映射时访问元素的顺序被定义为不可预测的,即使在使用相同的映射多次运行相同的循环时也是如此。代码不应假设元素以任何特定顺序被访问。

此更改意味着依赖于迭代顺序的代码很可能在早期中断并在成为问题之前很久就被修复。同样重要的是,它允许映射实现即使在程序使用范围循环从映射中选择元素时也能确保更好的映射平衡。

    m := map[string]int{"Sunday": 0, "Monday": 1}
    for name, value := range m {
        // This loop should not assume Sunday will be visited first.
        f(name, value)
    }

更新:这是工具无法提供帮助的一种更改。大多数现有代码都不会受到影响,但某些程序可能会中断或行为异常;我们建议手动检查映射上的所有范围语句,以验证它们不依赖于迭代顺序。标准存储库中有一些此类示例;它们已被修复。请注意,依赖于迭代顺序(未指定)本身就是不正确的。此更改将不可预测性编纂为规定。

多重赋值

语言规范长期以来一直保证在赋值中,所有右侧表达式都将在任何左侧表达式被赋值之前进行评估。为了保证可预测的行为,Go 1 进一步细化了规范。

如果赋值语句的左侧包含需要评估的表达式,例如函数调用或数组索引操作,则这些表达式将全部使用通常的从左到右规则在任何变量被分配其值之前完成。一旦所有内容都已评估,实际的赋值将按从左到右的顺序进行。

这些示例说明了行为。

    sa := []int{1, 2, 3}
    i := 0
    i, sa[i] = 1, 2 // sets i = 1, sa[0] = 2

    sb := []int{1, 2, 3}
    j := 0
    sb[j], j = 2, 1 // sets sb[0] = 2, j = 1

    sc := []int{1, 2, 3}
    sc[0], sc[0] = 1, 2 // sets sc[0] = 1, then sc[0] = 2 (so sc[0] = 2 at end)

更新:这是一个工具无法帮助解决的更改,但不太可能造成破坏。此更改未破坏标准存储库中的任何代码,并且依赖于先前未指定行为的代码已经不正确。

返回值和被遮蔽的变量

一个常见的错误是在对与结果变量同名但不是同一个变量的变量进行赋值后使用return(不带参数)。这种情况称为遮蔽:结果变量已被在内部作用域中声明的另一个同名变量遮蔽。

在具有命名返回值的函数中,Go 1 编译器不允许在任何命名返回值在 return 语句处被遮蔽的情况下使用不带参数的 return 语句。(这不是规范的一部分,因为这是我们仍在探索的一个领域;这种情况类似于编译器拒绝不以显式 return 语句结束的函数。)

此函数隐式返回一个被遮蔽的返回值,编译器将拒绝它

func Bug() (i, j, k int) {
    for i = 0; i < 5; i++ {
        for j := 0; j < 5; j++ { // Redeclares j.
            k += i * j
            if k > 100 {
                return // Rejected: j is shadowed here.
            }
        }
    }
    return // OK: j is not shadowed here.
}

更新:以这种方式遮蔽返回值的代码将被编译器拒绝,需要手动修复。标准存储库中出现的少数几个案例大多是错误。

复制包含未导出字段的结构体

旧语言不允许包复制包含属于不同包的未导出字段的结构体值。但是,方法接收器有一个必需的例外;此外,copyappend 的实现从未遵守该限制。

Go 1 将允许包复制包含来自其他包的未导出字段的结构体值。除了解决不一致性之外,此更改还允许一种新的 API:一个包可以返回一个不透明的值,而无需使用指针或接口。新的 time.Timereflect.Value 实现是利用此新属性的类型的示例。

例如,如果包p包含以下定义,

    type Struct struct {
        Public int
        secret int
    }
    func NewStruct(a int) Struct {  // Note: not a pointer.
        return Struct{a, f(a)}
    }
    func (s Struct) String() string {
        return fmt.Sprintf("{%d (secret %d)}", s.Public, s.secret)
    }

导入p的包可以随意分配和复制类型为p.Struct的值。在幕后,未导出字段将被分配和复制,就像它们被导出一样,但客户端代码永远不会意识到它们。代码

    import "p"

    myStruct := p.NewStruct(23)
    copyOfMyStruct := myStruct
    fmt.Println(myStruct, copyOfMyStruct)

将显示结构体的 secret 字段已复制到新值中。

更新:这是一个新功能,因此现有代码不需要更改。

相等性

在 Go 1 之前,该语言没有定义结构体和数组值的相等性。这意味着,除其他事项外,结构体和数组不能用作映射键。另一方面,Go 确实定义了函数和映射值的相等性。在存在闭包的情况下,函数相等性是有问题的(两个闭包何时相等?),而映射相等性比较的是指针,而不是映射的内容,这通常不是用户想要的。

Go 1 解决了这些问题。首先,可以比较结构体和数组的相等性和不相等性(==!=),因此可以将其用作映射键,前提是它们由也定义了相等性的元素组成,使用逐元素比较。

    type Day struct {
        long  string
        short string
    }
    Christmas := Day{"Christmas", "XMas"}
    Thanksgiving := Day{"Thanksgiving", "Turkey"}
    holiday := map[Day]bool{
        Christmas:    true,
        Thanksgiving: true,
    }
    fmt.Printf("Christmas is a holiday: %t\n", holiday[Christmas])

其次,Go 1 删除了函数值的相等性定义,除了与nil的比较。最后,映射相等性也消失了,除了与nil的比较。

请注意,切片的相等性仍然未定义,因为计算通常是不可行的。另请注意,有序比较运算符(< <= > >=)对于结构体和数组仍然未定义。

更新:结构体和数组相等性是一项新功能,因此现有代码无需更改。依赖于函数或映射相等性的现有代码将被编译器拒绝,需要手动修复。受影响的程序不多,但修复可能需要一些重新设计。

包层次结构

Go 1 解决了旧标准库中的许多缺陷,并清理了许多包,使其在内部更加一致和可移植。

本节介绍 Go 1 中包是如何重新排列的。有些包已移动,有些包已重命名,有些包已被删除。新包将在后面的章节中介绍。

包层次结构

Go 1 具有重新排列的包层次结构,该层次结构将相关项分组到子目录中。例如,utf8utf16 现在位于 unicode 的子目录中。此外,某些包已移至code.google.com/p/go 的子存储库中,而其他包则被彻底删除。

旧路径 新路径

asn1 encoding/asn1
csv encoding/csv
gob encoding/gob
json encoding/json
xml encoding/xml

exp/template/html html/template

big math/big
cmath math/cmplx
rand math/rand

http net/http
http/cgi net/http/cgi
http/fcgi net/http/fcgi
http/httptest net/http/httptest
http/pprof net/http/pprof
mail net/mail
rpc net/rpc
rpc/jsonrpc net/rpc/jsonrpc
smtp net/smtp
url net/url

exec os/exec

scanner text/scanner
tabwriter text/tabwriter
template text/template
template/parse text/template/parse

utf8 unicode/utf8
utf16 unicode/utf16

请注意,旧cmathexp/template/html 包的包名称已更改为 cmplxtemplate

更新:运行go fix 将更新所有导入和包重命名,这些导入和包重命名适用于保留在标准存储库中的包。需要手动编辑导入不再位于标准存储库中的包的程序。

包树 exp

由于它们未标准化,因此标准 Go 1 发行版中将不提供exp目录下的包,尽管它们将在存储库 中以源代码形式提供,供希望使用它们的开发人员使用。

在 Go 1 发布时,一些包已移至exp

(EscapeStringUnescapeString 类型保留在包 html 中。)

所有这些包都使用相同的名称,并在前面加上exp/前缀:exp/ebnf 等。

此外,utf8.String 类型已移至其自己的包 exp/utf8string

最后,gotype 命令现在位于 exp/gotype 中,而 ebnflint 现在位于 exp/ebnflint 中。如果已安装它们,它们现在位于 $GOROOT/bin/tool 中。

更新:使用exp 中的包的代码需要手动更新,或者从具有exp 可用性的安装中编译。go fix 工具或编译器将抱怨此类用法。

包树 old

由于已弃用,因此标准 Go 1 发行版中将不提供old目录下的包,尽管它们将以源代码形式提供,供希望使用它们的开发人员使用。

它们在新位置中的包为

更新:使用现在位于old 中的包的代码需要手动更新,或者从具有old 可用性的安装中编译。go fix 工具将警告此类用法。

已删除的包

Go 1 完全删除了几个包

以及命令gotry

更新:使用container/vector 的代码应更新为直接使用切片。有关一些建议,请参阅Go 语言社区 Wiki。使用其他包的代码(应该几乎为零)需要重新考虑。

包移至子存储库

Go 1 已将许多包移至其他存储库,通常是主 Go 存储库 的子存储库。此表列出了旧的和新的导入路径


crypto/bcrypt code.google.com/p/go.crypto/bcrypt
crypto/blowfish code.google.com/p/go.crypto/blowfish
crypto/cast5 code.google.com/p/go.crypto/cast5
crypto/md4 code.google.com/p/go.crypto/md4
crypto/ocsp code.google.com/p/go.crypto/ocsp
crypto/openpgp code.google.com/p/go.crypto/openpgp
crypto/openpgp/armor code.google.com/p/go.crypto/openpgp/armor
crypto/openpgp/elgamal code.google.com/p/go.crypto/openpgp/elgamal
crypto/openpgp/errors code.google.com/p/go.crypto/openpgp/errors
crypto/openpgp/packet code.google.com/p/go.crypto/openpgp/packet
crypto/openpgp/s2k code.google.com/p/go.crypto/openpgp/s2k
crypto/ripemd160 code.google.com/p/go.crypto/ripemd160
crypto/twofish code.google.com/p/go.crypto/twofish
crypto/xtea code.google.com/p/go.crypto/xtea
exp/ssh code.google.com/p/go.crypto/ssh

image/bmp code.google.com/p/go.image/bmp
image/tiff code.google.com/p/go.image/tiff

net/dict code.google.com/p/go.net/dict
net/websocket code.google.com/p/go.net/websocket
exp/spdy code.google.com/p/go.net/spdy

encoding/git85 code.google.com/p/go.codereview/git85
patch code.google.com/p/go.codereview/patch

exp/wingui code.google.com/p/gowingui

更新:运行go fix 将更新这些包的导入以使用新的导入路径。依赖于这些包的安装需要使用go get 命令安装它们。

库的主要更改

本节介绍核心库的重大更改,这些更改会影响大多数程序。

错误类型和 errors 包

os.Error 在包os 中的位置主要是历史原因:错误最初是在实现包os 时出现的,当时它们似乎与系统相关。从那时起,很明显错误比操作系统更基本。例如,在os 依赖的包(如syscall)中使用Errors 会很好。此外,将Error 放在os 中会引入许多对os 的依赖项,否则这些依赖项将不存在。

Go 1 通过引入一个内置的error 接口类型和一个单独的errors 包(类似于bytesstrings)来解决这些问题,该包包含实用程序函数。它用errors.New 替换了os.NewError,使错误在环境中占据更中心的位置。

因此,广泛使用的String 方法不会导致意外满足error 接口,error 接口改为使用名称Error 作为该方法的名称

    type error interface {
        Error() string
    }

fmt 库会自动调用 Error 方法,就像它对 String 方法所做的那样,以便轻松打印错误值。

type SyntaxError struct {
    File    string
    Line    int
    Message string
}

func (se *SyntaxError) Error() string {
    return fmt.Sprintf("%s:%d: %s", se.File, se.Line, se.Message)
}

所有标准包都已更新为使用新的接口;旧的 os.Error 已移除。

一个新的包,errors,包含函数

func New(text string) error

用于将字符串转换为错误。它替换了旧的 os.NewError

    var ErrSyntax = errors.New("syntax error")

更新:运行 go fix 将更新几乎所有受此更改影响的代码。定义具有 String 方法的错误类型的代码需要手动更新,将方法重命名为 Error

系统调用错误

旧的 syscall 包(早于 os.Error 以及几乎所有其他内容)将错误作为 int 值返回。反过来,os 包转发了许多这些错误,例如 EINVAL,但在每个平台上使用一组不同的错误。这种行为令人不快且不可移植。

在 Go 1 中,syscall 包改为为系统调用错误返回 error。在 Unix 上,实现是通过 syscall.Errno 类型完成的,该类型满足 error 并替换旧的 os.Errno

影响 os.EINVAL 及其相关内容的更改在其他地方进行了描述。

更新:运行 go fix 将更新几乎所有受此更改影响的代码。无论如何,大多数代码都应该使用 os 包而不是 syscall,因此不会受到影响。

时间

在编程语言中,时间始终是一个难以很好支持的挑战。旧的 Go time 包使用 int64 作为单位,没有真正的类型安全,也没有区分绝对时间和持续时间。

因此,Go 1 库中最重要的更改之一是对 time 包进行了彻底的重新设计。不再使用 int64 表示纳秒数的整数,也不再使用单独的 *time.Time 类型来处理小时和年份等人类单位,现在有两个基本类型:time.Time(一个值,所以 * 已移除),表示时间点;以及 time.Duration,表示时间间隔。两者都具有纳秒分辨率。Time 可以表示从远古时代到遥远未来的任何时间,而 Duration 只能跨越大约正负 290 年。这些类型上有一些方法,以及一些有用的预定义常量持续时间,例如 time.Second

新方法中包括诸如 Time.Add 之类的方法,它将 Duration 添加到 Time 中,以及 Time.Sub,它减去两个 Times 以产生 Duration

最重要的语义变化是 Unix 纪元(1970 年 1 月 1 日)现在仅与那些提及 Unix 的函数和方法相关:time.Unix 以及 Time 类型的 UnixUnixNano 方法。特别是,time.Now 返回 time.Time 值,而不是在旧 API 中返回自 Unix 纪元以来的纳秒数整数。

// sleepUntil sleeps until the specified time. It returns immediately if it's too late.
func sleepUntil(wakeup time.Time) {
    now := time.Now() // A Time.
    if !wakeup.After(now) {
        return
    }
    delta := wakeup.Sub(now) // A Duration.
    fmt.Printf("Sleeping for %.3fs\n", delta.Seconds())
    time.Sleep(delta)
}

新的类型、方法和常量已传播到所有使用时间的标准包中,例如 os 及其对文件时间戳的表示。

更新go fix 工具将更新许多对旧 time 包的使用,以使用新的类型和方法,尽管它不会替换表示每秒纳秒数的值,例如 1e9。此外,由于某些生成值的类型更改,修复工具重写的某些表达式可能需要进一步的手动编辑;在这种情况下,重写将包含旧功能的正确函数或方法,但类型可能不正确或需要进一步分析。

库的次要更改

本节描述较小的更改,例如对不太常用的包的更改或仅影响少数程序(除了需要运行 go fix 之外)的更改。此类别包括 Go 1 中新增的包。总的来说,它们提高了可移植性、规范化了行为,并使接口更现代化和更 Go 风格。

archive/zip 包

在 Go 1 中,*zip.Writer 不再具有 Write 方法。它的存在是一个错误。

更新:受影响的少量代码将被编译器捕获,并且必须手动更新。

bufio 包

在 Go 1 中,bufio.NewReaderSizebufio.NewWriterSize 函数不再为无效大小返回错误。如果参数大小太小或无效,则会进行调整。

更新:运行 go fix 将更新将错误赋值给 _ 的调用。未修复的调用将被编译器捕获,并且必须手动更新。

compress/flate、compress/gzip 和 compress/zlib 包

在 Go 1 中,compress/flatecompress/gzipcompress/zlib 中的 NewWriterXxx 函数如果采用压缩级别,则都返回 (*Writer, error),否则返回 *Writer。包 gzipCompressorDecompressor 类型已重命名为 WriterReader。包 flateWrongValueError 类型已被移除。

更新 运行 go fix 将更新旧名称和将错误赋值给 _ 的调用。未修复的调用将被编译器捕获,并且必须手动更新。

crypto/aes 和 crypto/des 包

在 Go 1 中,已移除 Reset 方法。Go 不保证内存不会被复制,因此此方法具有误导性。

特定于密码的类型 *aes.Cipher*des.Cipher*des.TripleDESCipher 已被移除,取而代之的是 cipher.Block

更新:移除对 Reset 的调用。将特定密码类型的使用替换为 cipher.Block。

crypto/elliptic 包

在 Go 1 中,elliptic.Curve 已成为一个接口,以允许替代实现。曲线参数已移至 elliptic.CurveParams 结构。

更新*elliptic.Curve 的现有用户需要更改为简单的 elliptic.Curve。对 MarshalUnmarshalGenerateKey 的调用现在是 crypto/elliptic 中的函数,它们将 elliptic.Curve 作为其第一个参数。

crypto/hmac 包

在 Go 1 中,特定于哈希的函数(例如 hmac.NewMD5)已从 crypto/hmac 中移除。相反,hmac.New 获取一个返回 hash.Hash 的函数,例如 md5.New

更新:运行 go fix 将执行所需的更改。

crypto/x509 包

在 Go 1 中,crypto/x509 中的 CreateCertificate 函数和 CreateCRL 方法已更改为采用 interface{},而之前则采用 *rsa.PublicKey*rsa.PrivateKey。这将允许将来实现其他公钥算法。

更新:无需更改。

encoding/binary 包

在 Go 1 中,binary.TotalSize 函数已被 Size 替换,后者采用 interface{} 参数而不是 reflect.Value

更新:受影响的少量代码将被编译器捕获,并且必须手动更新。

encoding/xml 包

在 Go 1 中,xml 包的设计更接近其他编组包,例如 encoding/gob

旧的 Parser 类型已重命名为 Decoder,并具有一个新的 Decode 方法。还引入了 Encoder 类型。

函数 MarshalUnmarshal 现在使用 []byte 值。要使用流,请使用新的 EncoderDecoder 类型。

在编组或解组值时,字段标记中支持的标志格式已更改,使其更接近 json 包(`xml:"name,flag"`)。字段标记、字段名称与 XML 属性和元素名称之间的匹配现在区分大小写。如果存在 XMLName 字段标记,则也必须与正在编组的 XML 元素的名称匹配。

更新:运行 go fix 将更新包的大多数使用情况,但 Unmarshal 的某些调用除外。必须特别注意字段标记,因为修复工具不会更新它们,如果未手动修复,则在某些情况下它们会静默错误。例如,旧的 "attr" 现在写成 ",attr",而普通的 "attr" 仍然有效,但含义不同。

expvar 包

在 Go 1 中,已移除 RemoveAll 函数。*Map 上的 Iter 函数和 Iter 方法已被 Do(*Map).Do 替换。

更新:大多数使用 expvar 的代码无需更改。很少使用的 Iter 代码可以更新为将闭包传递给 Do 以实现相同的效果。

flag 包

在 Go 1 中,接口 flag.Value 发生了细微变化。Set 方法现在返回 error 而不是 bool 来指示成功或失败。

还有一种新的标志类型 Duration,用于支持指定时间间隔的参数值。此类标志的值必须给出单位,就像 time.Duration 格式化它们一样:10s1h30m 等。

var timeout = flag.Duration("timeout", 30*time.Second, "how long to wait for completion")

更新:实现自己的标志的程序需要少量手动修复才能更新其 Set 方法。Duration 标志是新的,不会影响任何现有代码。

go/* 包

go 下的几个包的 API 进行了略微修改。

go/scannergo/parsergo/printergo/doc 包中引入了具体的 Mode 类型,用于配置模式标志。

已从 go/scanner 包中移除 AllowIllegalCharsInsertSemis 模式。它们主要用于扫描除 Go 源文件之外的文本。相反,应将 text/scanner 包用于此目的。

提供给扫描程序的 Init 方法的 ErrorHandler 现在只是一个函数,而不是一个接口。ErrorVector 类型已被移除,取而代之的是(现有的)ErrorList 类型,并且 ErrorVector 方法已被迁移。现在,客户端应该维护 ErrorList,而不是在扫描程序的客户端中嵌入 ErrorVector

go/parser 包提供的解析函数集已减少到主要解析函数 ParseFile,以及几个便利函数 ParseDirParseExpr

go/printer 包支持一种额外的配置模式 SourcePos;如果设置,打印机将发出 //line 注释,以便生成的输出包含原始源代码位置信息。新类型 CommentedNode 可用于提供与任意 ast.Node 关联的注释(到目前为止,只有 ast.File 携带注释信息)。

go/doc 包的类型名称已通过删除 Doc 后缀进行了简化:PackageDoc 现在为 PackageValueDocValue,等等。此外,所有类型现在都一致地具有 Name 字段(或在 Value 类型的情况下为 Names),并且 Type.Factories 已变为 Type.Funcs。无需再调用 doc.NewPackageDoc(pkg, importpath),包的文档是使用以下方法创建的:

    doc.New(pkg, importpath, mode)

其中新的 mode 参数指定操作模式:如果设置为 AllDecls,则考虑所有声明(而不仅仅是导出的声明)。NewFileDoc 函数已移除,CommentText 函数已变为 ast.CommentGroupText 方法。

go/token 包中,token.FileSet 方法 Files(最初返回 *token.File 的通道)已被迭代器 Iterate 替换,该迭代器接受一个函数参数。

go/build 包中,API 几乎完全被替换了。该包仍然计算 Go 包信息,但它不运行构建:CmdScript 类型已消失。(要构建代码,请改用新的 go 命令。)DirInfo 类型现在命名为 PackageFindTreeScanDirImportImportDir 替换。

更新:使用 go 中包的代码将需要手动更新;编译器将拒绝不正确的用法。与任何 go/doc 类型结合使用的模板可能需要手动修复;重命名的字段将导致运行时错误。

哈希包

在 Go 1 中,hash.Hash 的定义包含一个新方法 BlockSize。此新方法主要用于加密库。

hash.Hash 接口的 Sum 方法现在接受一个 []byte 参数,哈希值将附加到该参数中。可以通过向调用添加一个 nil 参数来重新创建先前的行为。

更新hash.Hash 的现有实现将需要添加 BlockSize 方法。逐字节处理输入的哈希可以实现 BlockSize 以返回 1。运行 go fix 将更新对 hash.Hash 的各种实现的 Sum 方法的调用。

更新:由于该包的功能是新的,因此无需更新。

HTTP 包

在 Go 1 中,http 包进行了重构,将一些实用程序放入 httputil 子目录中。HTTP 客户端很少需要这些部分。受影响的项目是

Request.RawURL 字段已被移除;它是一个历史遗留物。

HandleHandleFunc 函数以及 ServeMux 的类似命名方法,如果尝试注册相同的模式两次,则会引发恐慌。

更新:运行 go fix 将更新受影响的少数程序,但 RawURL 的使用除外,必须手动修复。

图像包

image 包进行了一些小的更改、重新排列和重命名。

大部分颜色处理代码已移至其自己的包 image/color 中。对于已移动的元素,会出现对称性;例如,image.RGBA 的每个像素都是一个 color.RGBA

旧的 image/ycbcr 包已合并,并进行了一些重命名,合并到 imageimage/color 包中。

旧的 image.ColorImage 类型仍在 image 包中,但已重命名为 image.Uniform,而 image.Tiled 已移除。

此表列出了重命名。


image.Color color.Color
image.ColorModel color.Model
image.ColorModelFunc color.ModelFunc
image.PalettedColorModel color.Palette

image.RGBAColor color.RGBA
image.RGBA64Color color.RGBA64
image.NRGBAColor color.NRGBA
image.NRGBA64Color color.NRGBA64
image.AlphaColor color.Alpha
image.Alpha16Color color.Alpha16
image.GrayColor color.Gray
image.Gray16Color color.Gray16

image.RGBAColorModel color.RGBAModel
image.RGBA64ColorModel color.RGBA64Model
image.NRGBAColorModel color.NRGBAModel
image.NRGBA64ColorModel color.NRGBA64Model
image.AlphaColorModel color.AlphaModel
image.Alpha16ColorModel color.Alpha16Model
image.GrayColorModel color.GrayModel
image.Gray16ColorModel color.Gray16Model

ycbcr.RGBToYCbCr color.RGBToYCbCr
ycbcr.YCbCrToRGB color.YCbCrToRGB
ycbcr.YCbCrColorModel color.YCbCrModel
ycbcr.YCbCrColor color.YCbCr
ycbcr.YCbCr image.YCbCr

ycbcr.SubsampleRatio444 image.YCbCrSubsampleRatio444
ycbcr.SubsampleRatio422 image.YCbCrSubsampleRatio422
ycbcr.SubsampleRatio420 image.YCbCrSubsampleRatio420

image.ColorImage image.Uniform

image 包的 New 函数(NewRGBANewRGBA64 等)采用 image.Rectangle 作为参数,而不是四个整数。

最后,还有新的预定义 color.Color 变量 color.Blackcolor.Whitecolor.Opaquecolor.Transparent

更新:运行 go fix 将更新受更改影响的几乎所有代码。

日志/syslog 包

在 Go 1 中,syslog.NewLogger 函数除了返回 log.Logger 之外,还会返回错误。

更新:受影响的少量代码将被编译器捕获,并且必须手动更新。

MIME 包

在 Go 1 中,mime 包的 FormatMediaType 函数已简化,使其与 ParseMediaType 保持一致。它现在采用 "text/html" 而不是 "text""html"

更新:受影响的少量代码将被编译器捕获,并且必须手动更新。

网络包

在 Go 1 中,各种 SetTimeoutSetReadTimeoutSetWriteTimeout 方法已被分别替换为 SetDeadlineSetReadDeadlineSetWriteDeadline。新方法不是以纳秒为单位获取适用于连接上任何活动的超时值,而是设置一个绝对截止时间(作为 time.Time 值),在此截止时间之后,读写将超时并且不再阻塞。

还有一些新函数 net.DialTimeout 用于简化网络地址拨号超时,以及 net.ListenMulticastUDP 用于允许多播 UDP 在多个侦听器上并发侦听。net.ListenMulticastUDP 函数替换了旧的 JoinGroupLeaveGroup 方法。

更新:使用旧方法的代码将无法编译,必须手动更新。语义更改使得修复工具难以自动更新。

操作系统包

Time 函数已被移除;调用方应使用 time 包中的 Time 类型。

Exec 函数已被移除;调用方应使用 syscall 包中的 Exec,如果可用。

ShellExpand 函数已重命名为 ExpandEnv

NewFile 函数现在采用 uintptr fd,而不是 int。文件上的 Fd 方法现在也返回 uintptr

os 包中不再有诸如 EINVAL 之类的错误常量,因为值的集合随底层操作系统而异。有一些新的可移植函数,如 IsPermission 用于测试常见的错误属性,以及一些新的错误值,其名称更符合 Go 风格,例如 ErrPermissionErrNotExist

Getenverror 函数已被移除。要区分不存在的环境变量和空字符串,请使用 os.Environsyscall.Getenv

Process.Wait 方法已删除其选项参数,并且关联的常量已从包中删除。此外,Wait 函数已消失;只有 Process 类型的此方法仍然存在。

Process.Wait 返回的 Waitmsg 类型已被更易移植的 ProcessState 类型替换,该类型具有访问器方法来恢复有关进程的信息。由于 Wait 的更改,ProcessState 值始终描述已退出的进程。可移植性问题以其他方式简化了接口,但 ProcessState.SysProcessState.SysUsage 方法返回的值可以断言为底层特定于系统的 数据结构,例如 Unix 上的 syscall.WaitStatussyscall.Rusage

更新:运行 go fix 将删除 Process.Wait 的零个参数。所有其他更改都将由编译器捕获,并且必须手动更新。

os.FileInfo 类型

Go 1 重新定义了 os.FileInfo 类型,将其从结构体更改为接口

    type FileInfo interface {
        Name() string       // base name of the file
        Size() int64        // length in bytes
        Mode() FileMode     // file mode bits
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()
        Sys() interface{}   // underlying data source (can return nil)
    }

文件模式信息已移至名为 os.FileMode 的子类型中,这是一个简单的整数类型,具有 IsDirPermString 方法。

文件模式和属性(例如 Unix 上的 i-number)的特定于系统的信息已完全从 FileInfo 中移除。相反,每个操作系统的 os 包都提供了 FileInfo 接口的实现,该接口具有一个 Sys 方法,该方法返回文件元数据的特定于系统表示形式。例如,要在 Unix 系统上发现文件的 i-number,请像这样解包 FileInfo

    fi, err := os.Stat("hello.go")
    if err != nil {
        log.Fatal(err)
    }
    // Check that it's a Unix file.
    unixStat, ok := fi.Sys().(*syscall.Stat_t)
    if !ok {
        log.Fatal("hello.go: not a Unix file")
    }
    fmt.Printf("file i-number: %d\n", unixStat.Ino)

假设(这不明智)"hello.go" 是一个 Unix 文件,则 i-number 表达式可以缩写为

    fi.Sys().(*syscall.Stat_t).Ino

FileInfo 的绝大多数用法只需要标准接口的方法。

os 包不再包含 POSIX 错误的包装器,例如 ENOENT。对于需要验证特定错误条件的少数程序,现在有布尔函数 IsExistIsNotExistIsPermission

    f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
    if os.IsExist(err) {
        log.Printf("%s already exists", name)
    }

更新:运行 go fix 将更新使用当前 os.FileInfoos.FileMode API 的旧等价物的代码。需要系统特定文件详细信息的代码需要手动更新。使用 os 包中旧 POSIX 错误值的代码将无法编译,也需要手动更新。

os/signal 包

Go 1 中的 os/signal 包用选择性 Notify 函数替换了 Incoming 函数,后者返回一个接收所有传入信号的通道,而前者则要求在现有通道上传递特定信号。

更新:代码必须手动更新。以下代码的字面翻译:

c := signal.Incoming()

c := make(chan os.Signal, 1)
signal.Notify(c) // ask for all signals

但大多数代码应该列出它想要处理的特定信号。

c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT)

path/filepath 包

在 Go 1 中,path/filepath 包的 Walk 函数已更改为采用类型为 WalkFunc 的函数值,而不是 Visitor 接口值。WalkFunc 统一了文件和目录的处理。

    type WalkFunc func(path string, info os.FileInfo, err error) error

即使对于无法打开的文件或目录,也会调用 WalkFunc 函数;在这种情况下,错误参数将描述失败。如果要跳过目录的内容,则函数应返回值 filepath.SkipDir

    markFn := func(path string, info os.FileInfo, err error) error {
        if path == "pictures" { // Will skip walking of directory pictures and its contents.
            return filepath.SkipDir
        }
        if err != nil {
            return err
        }
        log.Println(path)
        return nil
    }
    err := filepath.Walk(".", markFn)
    if err != nil {
        log.Fatal(err)
    }

更新:此更改简化了大多数代码,但具有细微的影响,因此受影响的程序需要手动更新。编译器将捕获使用旧接口的代码。

regexp 包

regexp 包已被重写。它具有相同的接口,但它支持的正则表达式的规范已从旧的“egrep”形式更改为 RE2 的规范。

更新:使用该包的代码应手动检查其正则表达式。

runtime 包

在 Go 1 中,runtime 包导出的许多 API 已被删除,取而代之的是其他包提供的功能。使用 runtime.Type 接口或其特定具体类型实现的代码现在应使用 reflect 包。使用 runtime.Semacquireruntime.Semrelease 的代码应使用通道或 sync 包中的抽象。runtime.Allocruntime.Freeruntime.Lookup 函数(为调试内存分配器而创建的不安全 API)没有替代品。

以前,runtime.MemStats 是一个全局变量,其中包含有关内存分配的统计信息,并且对 runtime.UpdateMemStats 的调用确保它是最新的。在 Go 1 中,runtime.MemStats 是一个结构体类型,代码应使用 runtime.ReadMemStats 获取当前统计信息。

该包添加了一个新函数 runtime.NumCPU,它返回操作系统内核报告的可用作并行执行的 CPU 数量。其值可以告知 GOMAXPROCS 的设置。runtime.Cgocallsruntime.Goroutines 函数已重命名为 runtime.NumCgoCallruntime.NumGoroutine

更新:运行 go fix 将更新函数重命名的代码。其他代码需要手动更新。

strconv 包

在 Go 1 中,strconv 包已进行了重大重构,使其更像 Go,更不像 C,尽管 Atoi 仍然存在(它类似于 int(ParseInt(x, 10, 0))),Itoa(x) 也一样(FormatInt(int64(x), 10))。还有一些新版本的函数可以追加到字节切片而不是返回字符串,以允许控制分配。

此表总结了重命名;有关完整详细信息,请参阅 包文档

旧调用 新调用

Atob(x) ParseBool(x)

Atof32(x) ParseFloat(x, 32)§
Atof64(x) ParseFloat(x, 64)
AtofN(x, n) ParseFloat(x, n)

Atoi(x) Atoi(x)
Atoi(x) ParseInt(x, 10, 0)§
Atoi64(x) ParseInt(x, 10, 64)

Atoui(x) ParseUint(x, 10, 0)§
Atoui64(x) ParseUint(x, 10, 64)

Btoi64(x, b) ParseInt(x, b, 64)
Btoui64(x, b) ParseUint(x, b, 64)

Btoa(x) FormatBool(x)

Ftoa32(x, f, p) FormatFloat(float64(x), f, p, 32)
Ftoa64(x, f, p) FormatFloat(x, f, p, 64)
FtoaN(x, f, p, n) FormatFloat(x, f, p, n)

Itoa(x) Itoa(x)
Itoa(x) FormatInt(int64(x), 10)
Itoa64(x) FormatInt(x, 10)

Itob(x, b) FormatInt(int64(x), b)
Itob64(x, b) FormatInt(x, b)

Uitoa(x) FormatUint(uint64(x), 10)
Uitoa64(x) FormatUint(x, 10)

Uitob(x, b) FormatUint(uint64(x), b)
Uitob64(x, b) FormatUint(x, b)

更新:运行 go fix 将更新受更改影响的几乎所有代码。
§ Atoi 仍然存在,但 AtouiAtof32 不存在,因此它们可能需要手动添加的强制转换;go fix 工具会发出警告。

模板包

templateexp/template/html 包已移至 text/templatehtml/template。更重要的是,这些包的接口已简化。模板语言相同,但“模板集”的概念消失了,并且包的函数和方法也相应地发生了变化,通常是通过消除。

模板对象可以包含多个命名的模板定义,而不是集,实际上为模板调用构建了命名空间。模板可以调用与其关联的任何其他模板,但只能调用与其关联的那些模板。关联模板的最简单方法是将它们一起解析,这在包的新结构下变得更容易。

更新:导入将由 fix 工具更新。单模板用途在其他方面基本上不受影响。需要手动更新协同使用多个模板的代码。示例 text/template 文档中可以提供指导。

testing 包

testing 包有一个类型 B,作为参数传递给基准测试函数。在 Go 1 中,B 有新的方法,类似于 T 的方法,使日志记录和故障报告成为可能。

func BenchmarkSprintf(b *testing.B) {
    // Verify correctness before running benchmark.
    b.StopTimer()
    got := fmt.Sprintf("%x", 23)
    const expect = "17"
    if expect != got {
        b.Fatalf("expected %q; got %q", expect, got)
    }
    b.StartTimer()
    for i := 0; i < b.N; i++ {
        fmt.Sprintf("%x", 23)
    }
}

更新:现有代码不受影响,尽管应该更新使用 printlnpanic 的基准测试以使用新方法。

testing/script 包

testing/script 包已被删除。它是一个废弃物。

更新:没有代码可能会受到影响。

unsafe 包

在 Go 1 中,函数 unsafe.Typeofunsafe.Reflectunsafe.Unreflectunsafe.Newunsafe.NewArray 已被删除;它们复制了 reflect 包提供的更安全的功能。

更新:使用这些函数的代码必须重写为使用 reflect 包。对 encoding/gob协议缓冲区库 的更改可能有助于作为示例。

url 包

在 Go 1 中,url.URL 类型的几个字段已被删除或替换。

String 方法现在可以预测地使用 URL 的所有字段重建编码的 URL 字符串。生成的字符串也不会再对密码进行转义。

Raw 字段已被删除。在大多数情况下,可以使用 String 方法代替。

旧的 RawUserinfo 字段由类型为 *net.UserinfoUser 字段替换。可以使用新的 net.Usernet.UserPassword 函数创建此类型的值。EscapeUserinfoUnescapeUserinfo 函数也消失了。

RawAuthority 字段已被删除。相同的信息可在 HostUser 字段中获得。

RawPath 字段和 EncodedPath 方法已被删除。根 URL(在模式后跟一个斜杠)中的路径信息现在仅以解码形式在 Path 字段中可用。有时,可能需要编码数据才能获取在解码过程中丢失的信息。这些情况必须通过访问构建 URL 的数据来处理。

具有非根路径的 URL,例如 "mailto:[email protected]?subject=Hi",也以不同的方式处理。OpaquePath 布尔字段已被删除,并引入了一个新的 Opaque 字符串字段来保存此类 URL 的编码路径。在 Go 1 中,引用的 URL 解析为

    URL{
        Scheme: "mailto",
        Opaque: "[email protected]",
        RawQuery: "subject=Hi",
    }

一个新的 RequestURI 方法已添加到 URL 中。

ParseWithReference 函数已重命名为 ParseWithFragment

更新:使用旧字段的代码将无法编译,必须手动更新。语义更改使得修复工具难以自动更新。

go 命令

Go 1 引入了 go 命令,这是一个用于获取、构建和安装 Go 包和命令的工具。go 命令不再使用 makefile,而是使用 Go 源代码查找依赖项并确定构建条件。大多数现有的 Go 程序将不再需要 makefile 才能构建。

有关 go 命令的入门信息,请参阅 如何编写 Go 代码,有关完整详细信息,请参阅 go 命令文档

更新:依赖于 Go 项目旧的基于 makefile 的构建基础设施(Make.pkgMake.cmd 等)的项目应切换到使用 go 命令构建 Go 代码,并在必要时重写其 makefile 以执行任何辅助构建任务。

cgo 命令

在 Go 1 中,cgo 命令 使用了不同的 _cgo_export.h 文件,该文件是为包含 //export 行的包生成的。_cgo_export.h 文件现在以 C 前导注释开头,以便导出的函数定义可以使用其中定义的类型。这会导致多次编译前导部分,因此使用 //export 的包不能在 C 前导部分放置函数定义或变量初始化。

打包发布

与 Go 1 相关的一个最重要的变化是预打包的可下载发行版的可用性。它们可用于许多架构和操作系统的组合(包括 Windows),并且列表将不断增长。安装细节在入门页面中描述,而发行版本身则列在下载页面上。