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. }
更新:以这种方式遮蔽返回值的代码将被编译器拒绝,需要手动修复。标准存储库中出现的少数几个案例大多是错误。
复制包含未导出字段的结构体
旧语言不允许包复制包含属于不同包的未导出字段的结构体值。但是,方法接收器有一个必需的例外;此外,copy
和 append
的实现从未遵守该限制。
Go 1 将允许包复制包含来自其他包的未导出字段的结构体值。除了解决不一致性之外,此更改还允许一种新的 API:一个包可以返回一个不透明的值,而无需使用指针或接口。新的 time.Time
和 reflect.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 具有重新排列的包层次结构,该层次结构将相关项分组到子目录中。例如,utf8
和 utf16
现在位于 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 |
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 |
请注意,旧cmath
和 exp/template/html
包的包名称已更改为 cmplx
和 template
。
更新:运行go
fix
将更新所有导入和包重命名,这些导入和包重命名适用于保留在标准存储库中的包。需要手动编辑导入不再位于标准存储库中的包的程序。
包树 exp
由于它们未标准化,因此标准 Go 1 发行版中将不提供exp
目录下的包,尽管它们将在存储库 中以源代码形式提供,供希望使用它们的开发人员使用。
在 Go 1 发布时,一些包已移至exp
下
ebnf
html
†go/types
(†EscapeString
和 UnescapeString
类型保留在包 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/netchan
更新:使用现在位于old
中的包的代码需要手动更新,或者从具有old
可用性的安装中编译。go
fix
工具将警告此类用法。
已删除的包
Go 1 完全删除了几个包
container/vector
exp/datafmt
go/typechecker
old/regexp
old/template
try
以及命令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
包(类似于bytes
和strings
)来解决这些问题,该包包含实用程序函数。它用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
类型的 Unix
和 UnixNano
方法。特别是,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.NewReaderSize
和 bufio.NewWriterSize
函数不再为无效大小返回错误。如果参数大小太小或无效,则会进行调整。
更新:运行 go fix
将更新将错误赋值给 _ 的调用。未修复的调用将被编译器捕获,并且必须手动更新。
compress/flate、compress/gzip 和 compress/zlib 包
在 Go 1 中,compress/flate
、compress/gzip
和 compress/zlib
中的 NewWriterXxx
函数如果采用压缩级别,则都返回 (*Writer, error)
,否则返回 *Writer
。包 gzip
的 Compressor
和 Decompressor
类型已重命名为 Writer
和 Reader
。包 flate
的 WrongValueError
类型已被移除。
更新 运行 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
。对 Marshal
、Unmarshal
和 GenerateKey
的调用现在是 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
类型。
函数 Marshal
和 Unmarshal
现在使用 []byte
值。要使用流,请使用新的 Encoder
和 Decoder
类型。
在编组或解组值时,字段标记中支持的标志格式已更改,使其更接近 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
格式化它们一样:10s
、1h30m
等。
var timeout = flag.Duration("timeout", 30*time.Second, "how long to wait for completion")
更新:实现自己的标志的程序需要少量手动修复才能更新其 Set
方法。Duration
标志是新的,不会影响任何现有代码。
go/* 包
go
下的几个包的 API 进行了略微修改。
在 go/scanner
、go/parser
、go/printer
和 go/doc
包中引入了具体的 Mode
类型,用于配置模式标志。
已从 go/scanner
包中移除 AllowIllegalChars
和 InsertSemis
模式。它们主要用于扫描除 Go 源文件之外的文本。相反,应将 text/scanner
包用于此目的。
提供给扫描程序的 Init
方法的 ErrorHandler
现在只是一个函数,而不是一个接口。ErrorVector
类型已被移除,取而代之的是(现有的)ErrorList
类型,并且 ErrorVector
方法已被迁移。现在,客户端应该维护 ErrorList
,而不是在扫描程序的客户端中嵌入 ErrorVector
。
go/parser
包提供的解析函数集已减少到主要解析函数 ParseFile
,以及几个便利函数 ParseDir
和 ParseExpr
。
go/printer
包支持一种额外的配置模式 SourcePos
;如果设置,打印机将发出 //line
注释,以便生成的输出包含原始源代码位置信息。新类型 CommentedNode
可用于提供与任意 ast.Node
关联的注释(到目前为止,只有 ast.File
携带注释信息)。
go/doc
包的类型名称已通过删除 Doc
后缀进行了简化:PackageDoc
现在为 Package
,ValueDoc
为 Value
,等等。此外,所有类型现在都一致地具有 Name
字段(或在 Value
类型的情况下为 Names
),并且 Type.Factories
已变为 Type.Funcs
。无需再调用 doc.NewPackageDoc(pkg, importpath)
,包的文档是使用以下方法创建的:
doc.New(pkg, importpath, mode)
其中新的 mode
参数指定操作模式:如果设置为 AllDecls
,则考虑所有声明(而不仅仅是导出的声明)。NewFileDoc
函数已移除,CommentText
函数已变为 ast.CommentGroup
的 Text
方法。
在 go/token
包中,token.FileSet
方法 Files
(最初返回 *token.File
的通道)已被迭代器 Iterate
替换,该迭代器接受一个函数参数。
在 go/build
包中,API 几乎完全被替换了。该包仍然计算 Go 包信息,但它不运行构建:Cmd
和 Script
类型已消失。(要构建代码,请改用新的 go
命令。)DirInfo
类型现在命名为 Package
。FindTree
和 ScanDir
被 Import
和 ImportDir
替换。
更新:使用 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 客户端很少需要这些部分。受影响的项目是
- ClientConn
- DumpRequest
- DumpRequestOut
- DumpResponse
- NewChunkedReader
- NewChunkedWriter
- NewClientConn
- NewProxyClientConn
- NewServerConn
- NewSingleHostReverseProxy
- ReverseProxy
- ServerConn
Request.RawURL
字段已被移除;它是一个历史遗留物。
Handle
和 HandleFunc
函数以及 ServeMux
的类似命名方法,如果尝试注册相同的模式两次,则会引发恐慌。
更新:运行 go fix
将更新受影响的少数程序,但 RawURL
的使用除外,必须手动修复。
图像包
image
包进行了一些小的更改、重新排列和重命名。
大部分颜色处理代码已移至其自己的包 image/color
中。对于已移动的元素,会出现对称性;例如,image.RGBA
的每个像素都是一个 color.RGBA
。
旧的 image/ycbcr
包已合并,并进行了一些重命名,合并到 image
和 image/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
函数(NewRGBA
、NewRGBA64
等)采用 image.Rectangle
作为参数,而不是四个整数。
最后,还有新的预定义 color.Color
变量 color.Black
、color.White
、color.Opaque
和 color.Transparent
。
更新:运行 go fix
将更新受更改影响的几乎所有代码。
日志/syslog 包
在 Go 1 中,syslog.NewLogger
函数除了返回 log.Logger
之外,还会返回错误。
更新:受影响的少量代码将被编译器捕获,并且必须手动更新。
MIME 包
在 Go 1 中,mime
包的 FormatMediaType
函数已简化,使其与 ParseMediaType
保持一致。它现在采用 "text/html"
而不是 "text"
和 "html"
。
更新:受影响的少量代码将被编译器捕获,并且必须手动更新。
网络包
在 Go 1 中,各种 SetTimeout
、SetReadTimeout
和 SetWriteTimeout
方法已被分别替换为 SetDeadline
、SetReadDeadline
和 SetWriteDeadline
。新方法不是以纳秒为单位获取适用于连接上任何活动的超时值,而是设置一个绝对截止时间(作为 time.Time
值),在此截止时间之后,读写将超时并且不再阻塞。
还有一些新函数 net.DialTimeout
用于简化网络地址拨号超时,以及 net.ListenMulticastUDP
用于允许多播 UDP 在多个侦听器上并发侦听。net.ListenMulticastUDP
函数替换了旧的 JoinGroup
和 LeaveGroup
方法。
更新:使用旧方法的代码将无法编译,必须手动更新。语义更改使得修复工具难以自动更新。
操作系统包
Time
函数已被移除;调用方应使用 time
包中的 Time
类型。
Exec
函数已被移除;调用方应使用 syscall
包中的 Exec
,如果可用。
ShellExpand
函数已重命名为 ExpandEnv
。
NewFile
函数现在采用 uintptr
fd,而不是 int
。文件上的 Fd
方法现在也返回 uintptr
。
os
包中不再有诸如 EINVAL
之类的错误常量,因为值的集合随底层操作系统而异。有一些新的可移植函数,如 IsPermission
用于测试常见的错误属性,以及一些新的错误值,其名称更符合 Go 风格,例如 ErrPermission
和 ErrNotExist
。
Getenverror
函数已被移除。要区分不存在的环境变量和空字符串,请使用 os.Environ
或 syscall.Getenv
。
Process.Wait
方法已删除其选项参数,并且关联的常量已从包中删除。此外,Wait
函数已消失;只有 Process
类型的此方法仍然存在。
Process.Wait
返回的 Waitmsg
类型已被更易移植的 ProcessState
类型替换,该类型具有访问器方法来恢复有关进程的信息。由于 Wait
的更改,ProcessState
值始终描述已退出的进程。可移植性问题以其他方式简化了接口,但 ProcessState.Sys
和 ProcessState.SysUsage
方法返回的值可以断言为底层特定于系统的 数据结构,例如 Unix 上的 syscall.WaitStatus
和 syscall.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
的子类型中,这是一个简单的整数类型,具有 IsDir
、Perm
和 String
方法。
文件模式和属性(例如 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
。对于需要验证特定错误条件的少数程序,现在有布尔函数 IsExist
、IsNotExist
和 IsPermission
。
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.FileInfo
和 os.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.Semacquire
或 runtime.Semrelease
的代码应使用通道或 sync
包中的抽象。runtime.Alloc
、runtime.Free
和 runtime.Lookup
函数(为调试内存分配器而创建的不安全 API)没有替代品。
以前,runtime.MemStats
是一个全局变量,其中包含有关内存分配的统计信息,并且对 runtime.UpdateMemStats
的调用确保它是最新的。在 Go 1 中,runtime.MemStats
是一个结构体类型,代码应使用 runtime.ReadMemStats
获取当前统计信息。
该包添加了一个新函数 runtime.NumCPU
,它返回操作系统内核报告的可用作并行执行的 CPU 数量。其值可以告知 GOMAXPROCS
的设置。runtime.Cgocalls
和 runtime.Goroutines
函数已重命名为 runtime.NumCgoCall
和 runtime.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
仍然存在,但 Atoui
和 Atof32
不存在,因此它们可能需要手动添加的强制转换;go
fix
工具会发出警告。
模板包
template
和 exp/template/html
包已移至 text/template
和 html/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)
}
}
更新:现有代码不受影响,尽管应该更新使用 println
或 panic
的基准测试以使用新方法。
testing/script 包
testing/script 包已被删除。它是一个废弃物。
更新:没有代码可能会受到影响。
unsafe 包
在 Go 1 中,函数 unsafe.Typeof
、unsafe.Reflect
、unsafe.Unreflect
、unsafe.New
和 unsafe.NewArray
已被删除;它们复制了 reflect
包提供的更安全的功能。
更新:使用这些函数的代码必须重写为使用 reflect
包。对 encoding/gob 和 协议缓冲区库 的更改可能有助于作为示例。
url 包
在 Go 1 中,url.URL
类型的几个字段已被删除或替换。
String
方法现在可以预测地使用 URL
的所有字段重建编码的 URL 字符串。生成的字符串也不会再对密码进行转义。
Raw
字段已被删除。在大多数情况下,可以使用 String
方法代替。
旧的 RawUserinfo
字段由类型为 *net.Userinfo
的 User
字段替换。可以使用新的 net.User
和 net.UserPassword
函数创建此类型的值。EscapeUserinfo
和 UnescapeUserinfo
函数也消失了。
RawAuthority
字段已被删除。相同的信息可在 Host
和 User
字段中获得。
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.pkg
、Make.cmd
等)的项目应切换到使用 go
命令构建 Go 代码,并在必要时重写其 makefile 以执行任何辅助构建任务。
cgo 命令
在 Go 1 中,cgo 命令 使用了不同的 _cgo_export.h
文件,该文件是为包含 //export
行的包生成的。_cgo_export.h
文件现在以 C 前导注释开头,以便导出的函数定义可以使用其中定义的类型。这会导致多次编译前导部分,因此使用 //export
的包不能在 C 前导部分放置函数定义或变量初始化。
打包发布
与 Go 1 相关的一个最重要的变化是预打包的可下载发行版的可用性。它们可用于许多架构和操作系统的组合(包括 Windows),并且列表将不断增长。安装细节在入门页面中描述,而发行版本身则列在下载页面上。