Go 1.1 介绍

开发 前端
这个新的发布版,Go 1.1,在保持兼容性的前提下添加了若干重要的(当然,向后兼容)语言变化,而库变化的清单也很长(也向后兼容),还有在编译器、库和运行时环境实现的主要工作。

Go 1.1 介绍

Go ***版(简称 Go 1 或 Go 1.0)发布于 2012 年三月,这个版本提供了稳定的 Go 语言和库。其稳定性让全世界 Go 用户社区和相关系统茁壮成长。从那时起,就发布了若干个“关键点”——1.0.1、1.0.2 和 1.0.3。这些点的发布修复了若干已知 bug,但是对于实现本身并没有进行修改。

这个新的发布版,Go 1.1,在保持兼容性的前提下添加了若干重要的(当然,向后兼容)语言变化,而库变化的清单也很长(也向后兼容),还有在编译器、库和运行时环境实现的主要工作。焦点是性能。测试并不是十分精确,但是对于许多测试程序来说都有着重要的、有时是戏剧性的性能改善。我们相信,通过升级 Go 的安装包,并且重新编译,许多用户的程序也能让人体会到这一改进。

这一文档汇总了从 Go 1 到 Go 1.1 的变化。虽然这个发布版有一些极为罕见的错误情况,而当这些情况在发生时必须被处理。在 Go 1.1 下运行,几乎不需要修改任何代码。下面描述了细节;参阅 64 位整数和 Unicode 文字的特别说明。

语言的变化

Go 的兼容性文档保证了用 Go 1 语言规范编写的程序仍然可以使用,并且可以会继续被维护。尽管有一些细节的错误情况已经被指出,但是规范本身的完善还是相当有趣的。同时还增加了一些语言的新特性。

整数除以零

在 Go 1 中,整数被一个常量零整除会产生一个运行时 panic:

  1. func f(x intint {  
  2.     return x/0  

在 Go 1.1 中,一个整数被一个常量零整除不是合法的程序,因此这会是一个编译时错误。

代用的 Unicode 文字

细化了 string 和 rune 文字的定义,以便将代用部分排除在合法的 Unicode 编码值以外。参阅 Unicode 部分了解更多信息。

方法值

现在 Go 1.1 实现了方法值,也就是将函数绑定在特定的接收者的值上。例如,有一个Writer 的值 w,表达式 w.Write,是一个方法值,作为用于向 w 写入的函数;这与函数文法中对 w 进行闭包是等价的:

  1. func (p []byte) (n int, err error) {  
  2.     return w.Write(p)  

方法值与方法表达式是不同的,方法表达式从方法中利用指定的类型构造了一个函数;方法表达式 (*bufio.Writer).Write 与***个参数类型指定为 (*bufio.Writer) 的函数等价:

  1. func (w *bufio.Writer, p []byte) (n int, err error) {  
  2.     return w.Write(p)  

更新:已有代码不受影响;这个变动是严格的向后兼容。

Return requirements

在 Go 1.1 之前,一个函数返回一个值必须明确的在函数结束时“return”或调用 panic;这是一个让程序明确函数的概念的简单的途径。但是,显然有许多情况***的“return”没有必要,例如, 一个只有死循环“for”的函数。

在 Go 1.1 中,关于***的“return”语句的规则更加宽松。它引入了一个终止语句的概念,它保证了在函数中这个语句总是***被执行。例如在没有条件的“for”循环中,也没有“if-else”语句用来在中间通过“return”结束。那么函数的***一个语句可以在语法上被认为是终止语句,而不需要***的“return”语句。

注意这个规则纯粹是语法上的:它并不关注代码中的值,因此也没有复杂的分析。

更新:这个变动是向后兼容的,不过有着多余“return”语句或调用 panic 的已有代码可能需要手工处理一下。这些代码可用 go vet 来标识。

实现和工具的变更

命令行参数解析

在 gc 工具链中,编译器和链接器现在使用与 Go 的 flag 包一致的命令行参数解析规则,而与传统的 Unix 参数解析背道而驰。这可能会对直接调用工具的脚本产生影响。例如,go tool 6c -Fw -Dfoo 现在必须写为 go tool 6c -F -w -D foo。

在 64 位平台上的整数大小

该语言允许根据具体实现选择 int 类型和 uint 类型是 32 或 64 位的。之前 Go 的实现是在所有系统上都让 int 和 uint 是 32 位的。现在 gc 和 gccgo 的实现都让 int 和 uint 在如 AMD64/x86-64 这样的平台上是 64 位的。抛开别的不说,单这个就使得 slice 在 64 位平台上可以分配超过 20 亿的元素。

更新:大多数程序不会受到这个的影响。 由于 Go 不允许不同数字类型之间的隐式转换,不会有程序在编译时报错。然而,那些隐式假设 int 是 32 位的程序,在行为上可能发生变化。例如,这个程序在 64 位系统中会打印正数,在 32 位系统中会打印复数:

  1. x := ^uint32(0) // x is 0xffffffff  
  2. i := int(x)     // i is -1 on 32-bit systems, 0xffffffff on 64-bit  
  3. fmt.Println(i) 

要保留 32 位的符号(在所有系统上都是 -1)应该用下面的具有可移植性的代码代替:

  1. i := int(int32(x)) 

Unicode

为了能够表达 UTF-16 中超过 65535 的编码值,Unicode 定义了代用部分,一个仅用于组装更大的值的编码值范围,且仅在 UTF-16 中。在这个代用范围内的编码值如果用于其他任何情况都是非法的,如作为 UTF-8 编码,或作为独立的 UTF-16 编码。例如在遇到将一个 rune 转换成 UTF-8 时,它被当作一个编码错误对待,并产生一个替代的 rune,utf8.RuneError, U+FFFD。

这个程序,

  1. import "fmt" 
  2.    
  3. func main() {  
  4.     fmt.Printf("%+q\n", string(0xD800))  

在 Go 1.0 中打印“\ud800”,但在 Go 1.1 中打印“\ufffd”。

半个代用 Unicode 值现在在 rune 和 string 常量中都是非法的,因此如“\ud800”和“\ud800”的常量现在会被编译器拒绝。当编写为独立的 UTF-8 编码的字节时,这样字符串还是可以被创建的,例如“\xed\xa0\x80”。然而,当这个字符串被作为一个 rune 序列解码时,比如在 range 循环中,它只会生成 utf8.RuneError 值。

Unicode 字节顺序让 U+FFFE 和 U+FEFF 在 UTF-8 编码下可以作为 Go 源码的***个字符出现。虽然在字节顺序未设定的 UTF-8 编码中,它是完全不必要的,不过有些编辑器会将其作为“魔法数值”添加进去,用来标识一个 UTF-8 编码的文件。

更新:大多数程序不会受到代用变更的影响。基于旧的行为的程序应当通过修改来避免问题。字节顺序标识的变更是严格向后兼容的。

gc 汇编

基于如 int 到 64 位和其他一些变化,在 gc 工具链的函数参数的栈布局发生了变化。使用汇编编写的函数至少需要一个 frame 指针偏移量。

更新:现在 go vet 命令可以检查用汇编实现的函数是否匹配 Go 的函数原型。

go 命令的变化

为了让新的 Go 用户获得更好的体验,go 命令做了若干改动。

首先,当编译、测试或运行 Go 代码的时候,go 命令会给出更多的错误信息细节,当一个包无法被定位时,会列出搜索的路径清单。

  1. $ go build foo/quxx  
  2. can't load packagepackage foo/quxx: cannot find package "foo/quxx" in any of:  
  3.         /home/you/go/src/pkg/foo/quxx (from $GOROOT)  
  4.         /home/you/src/foo/quxx (from $GOPATH) 

其次,go get 命令不再允许下载包源码时,将 $GOROOT 作为默认的目的路径。要使用 go get 命令,必须有一个合法的 $GOPATH。

  1. $ GOPATH= go get code.google.com/p/foo/quxx  
  2. package code.google.com/p/foo/quxx: cannot download, $GOPATH not set. For more details see: go help gopath 

***,作为前面变化的结果,go get 命令会在 $GOPATH 和 $GOROOT 设置为相同值的时候报错。

  1. $ GOPATH=$GOROOT go get code.google.com/p/foo/quxx  
  2. warning: GOPATH set to GOROOT (/home/User/go) has no effect  
  3. package code.google.com/p/foo/quxx: cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath 

go test 命令的变化

go test 命令在进行性能测试时不再删除二进制内容,以便更容易的分析性能测试。实现上是在运行的时候设置了 -c 参数。

  1. $ go test -cpuprofile cpuprof.out mypackage 

在 go test 运行之后,mypackage.test 将会留在目录中。

go test 命令现在可以报告 goroutine 在哪里阻塞的测试信息,也就是说,它们在哪一直等着某个事件,例如一个 channel 通讯之类的。当用 -blockprofile 开启 go test 的阻塞测试时,就会展示这些信息。 运行 go help test 了解更多信息。

go fix 命令的变化

fix 命令通常以 go fix 执行,不再提供从 Go1 之前的版本升级到 Go 1 API 的功能。如果要升级 Go 1 之前的代码到 Go 1.1,首先应当使用 Go 1.0 的工具链,将代码转化到 Go 1.0。

性能

用 Go 1.1 的 gc 工具集编译出来的代码的性能对于大多数 Go 程序来说应当有显著的提升。一般来说,与 Go 1.0 相比,大约有 30%-40% 的提升,有时甚至更高,当然也会比这个值低,甚至没有提升。对于工具和库来说,有太多的小的性能驱使的改动,以至于无法将它们全部列在这里。不过下面的主要变更还是有必要留意的:

gc 编译器在大多数情况下都会生成较好的代码,尤其是在 32 位 Intel 架构下的浮点值。

·gc 编译器做了更多的内连,包括在运行时的一些操作,例如 append 和接口转换。

·Go 的 map 有了新的实现,在内存复制和 CPU 时间上有了重大的改进。

·垃圾回收实现了更多的并行,这可以降低在多 CPU 环境下运行的程序的延迟。

·垃圾回收同时也更加精准,这增加了一点 CPU 时间开销,但是极大的降低了堆的大小,尤其是在 32 位的架构下。

·通过紧密结合运行时和网络库,在网络操作时需要的上下文切换会更少。

#p#

标准库的变化

bufio.Scanner

在 bufio 包中有多种方式获取文本输入,ReadBytesReadString 和特别的 ReadLine,对于简单的目的这些都有些过于复杂了。在 Go 1.1 中,添加了一个新类型,Scanner,以便更容易的处理如按行读取输入序列或空格分隔的词等,这类简单的任务。它终结了如输入一个很长的有问题的行这样的输入错误,并且提供了简单的默认行为:基于行的输入,每行都剔除分隔标识。这里的代码展示来一次输入一行:

  1. scanner := bufio.NewScanner(os.Stdin)  
  2. for scanner.Scan() {  
  3.     fmt.Println(scanner.Text()) // Println will add back the final '\n'  
  4. }  
  5. if err := scanner.Err(); err != nil {  
  6.     fmt.Fprintln(os.Stderr, "reading standard input:", err)  

输入的行为可以通过一个函数控制,来控制输入的每个部分(参阅 SplitFunc 的文档),但是对于复杂的问题或持续传递错误的,可能还是需要原有接口。

net

在 net 包中的协议特定的解析器之前对传递入的网络名很宽松。虽然文档明确指出对于ResolveTCPAddr 合法的网络名只有“tcp”,“tcp4”和“tcp6”,Go 1.0 的实现对于任何字符串都会接受。而 Go 1.1 的实现,如果网络名不在这些字符串中,就会返回一个错误。这对于其他协议特定的解析器 ResolveIPAddrResolveUDPAddr 和ResolveUnixAddr 也是一样。

之前的的实现,ListenUnixgram 返回一个 UDPConn 作为接收连接的端点。在 Go 1.1 的实现里,用 UnixConn 来代替,这允许用它的 ReadFrom 和 WriteTo 方法读写。

数据结构 IPAddr、TCPAddr 和 UDPAddr 添加了一个叫做 Zone 的新字符串字段。由于新的字段,使用没有标签的复合文法(例如 net.TCPAddr{ip, port})的代码代替有标签的文法(net.TCPAddr{IP: ip, Port: port})会出错。Go 1 的兼容性规则允许这个变化:客户端代码必须使用标签化的文法以避免这种破坏。

更新:为了修正由于新的结构体字段带来的破坏,go fix 将会重写这些类型的代码以添加标签。更通用的是,go vet 将会标识出所有应当使用字段标签的复合文法。

reflect

reflect 包有若干重大改进。

现在用 reflect 包返回一个“select”语句是可能的;参阅 Select 和 SelectCase 了解更多细节。

新的方法 Value.Convert(或 Type.ConvertibleTo)提供了对一个 Value 进行 Go 的转换和类型断言操作(或者是检测这种可能性)的函数方式。

新的函数 MakeFunc 创建了一个使得在已有 Value 上调用函数更加容易的封装函数,可以用于标准的 Go 参数的转换,例如将一个 int 传递为 interface{}。

***,新的函数 ChanOfMapOf 和 SliceOf 可以从已有类型中构造新 Type,例如在仅提供 T 的情况下构造 []T。

time

之前的 time 包在 FreeBSD、Linux、NetBSD、OS X 和 OpenBSD 上精确到微秒。Go 1.1 在这些操作系统上的实现可以精确到纳秒。程序用微妙的精确度想外部写入再读出,若覆盖掉原有值的话,将会产生精度的损失。Time 有两个新方法,Round 和Truncate,可以用来在向外部存储写入前,从时间里去除精度。

新方法 YearDay 返回指定 time 值在一年中的某天的唯一整数序数。

Timer 类型有一个新方法 Reset,让定时器在指定的间隔后过期。

***,一个新函数 ParseInLocation 与已有的 Parse 类似,不过会忽略解析的字符串中的时区信息,而使用传入的位置(时区)来解析时间。这个函数解决了时间 API 中常见的混乱情况。

更新:对于那些使用更低精度的外部格式来读写时间的代码,应当修改用新的方法。

Exp 旧的代码树移动到 go.exp 和 go.text 子版本库

为了让使用二进制发布版的用户在需要的时候访问更加容易,不包含在二进制发布版的 exp 和旧的源码树被移动到新的子版本库 code.google.com/p/go.exp。举例来说,如果要访问 ssa 包,执行

  1. $ go get code.google.com/p/go.exp/ssa 

然后在 Go 代码中,

  1. import "code.google.com/p/go.exp/ssa" 

旧的包 exp/norm 也迁移到了新的版本库 go.text,这里包含了正在开发的 Unicode API 和其他文本相关的包。

库的微小变更

下面的清单列出了库的微小改动,大多数是一些增强。对于每个变更可参阅包相关的文档了解更多信息。

bytes 包有两个新函数,TrimPrefix 和 TrimSuffix,含义不言而喻。同样,Buffer 类型有一个新方法 Grow,提供了一些控制缓存内部内存分配的能力。***,Reader 类型现在有 WriteTo 方法,因此它也实现了 io.WriterTo 接口。

crypto/hmac 有一个新函数,Equal,来比较两个 MAC。

crypto/x509 包现在支持 PEM 块(实例参阅 DecryptPEMBlock),以及一个新的函数 ParseECPrivateKey 用来解析椭圆曲线私钥。

database/sql 包在 DB 类型上有了新的 Ping 方法用于检测连接的健康状况。

database/sql/driver 有了一个新的 Queryer 接口,这样 Conn 可以通过实现该接口对性能做一些改进。

encoding/json 包的 Decoder 有了新方法 Buffered,以提供访问在其缓存内剩余数据的功能,同样新方法 UseNumber 会将一个值解码为其实是字符串的新类型 Number,而不是一个 float64。

encoding/xml 包有了一个新函数 EscapeText,用于输出 escape 过的 XML,Encoder 的方法 Inden 则专门用于输出带缩进的格式。

在 go/ast 包中,新类型 CommentMap 和其关联的方法使得从 Go 程序中分离和处理注释变得更加容易。

在 go/doc 包中,解析器现在可以更好的跟踪一些如 TODO 这样的标识,godoc 命令可以根据 -notes 参数选择过滤或呈现这些信息。

一个新的包,go/format,为程序提供了更加方便的方式来获得 gofmt 的格式化的能力。它有两个函数,Node 用来格式化 Go 的解析 Node,而 Source 用来格式化 Go 的源代码。

html/template 包中没有文档并且只部分实现的“noescape”特性被移除;那些依赖它的程序会被破坏。

io 包现在将 io.ByteWriter 接口导出,用以满足一次写一个字节这样的常见功能。

log/syslog 包现在更好的提供了系统特定的日志功能。

math/big 包的 Int 类型现在有了方法 MarshalJSON 和 UnmarshalJSON 用以转换到或从 JSON 格式转换。同样,Int 现在可以通过 Uint64 和 SetUint64 直接转换到 uint64 或从 uint64 转换,而 Rat 通过 Float64 and SetFloat64.

mime/multipart 包的 Writer 有了新的方法,SetBoundary 用来定义包输出的边界分隔。

net 包的 ListenUnixgram 函数修改了返回值的类型:现在它返回 UnixConn 而不是 UDPConn,这明显是 Go 1.0 的一个错误。因此这个 API 的变更修复了一个 bug,这符合 Go 1 的兼容性规则。

net 包包含了一个新函数,DialOpt,为 Dial 增加选项。每个选项都由新的接口 DialOption 体现。新的函数 DeadlineTimeoutNetwork 和 LocalAddress 然会一个 DialOption。

net 增加了带区域验证的本地 IPv6 地址的支持,如 fe80::1%lo0。地址结构体 IPAddrUDPAddr 和 TCPAddr 将区域信息记录在一个新的字段里,那些需要字符串格式作为地址的函数,例如 DialResolveIPAddrResolveUDPAddr 和 ResolveTCPAddr 现在接受带区域验证的格式。

net 包添加了 LookupNS 作为解析函数。LookupNS 根据主机名返回一个 NS records 。

net 包向 IPConnReadMsgIP 和 WriteMsgIP)和 UDPConnReadMsgUDP 和 WriteMsgUDP)加了指定协议的读写方法。还有个 PacketConn 的特别版本的 ReadFrom 和 WriteTo 方法,提供了访问数据包的带外数据的能力。

net 为 UnixConn 添加了方法以便半关闭连接(CloseRead 和 CloseWrite),这与 TCPConn 的已有方法匹配。

net/http 包包含了若干新增。ParseTime 解析一个时间字符串,会尝试若干种常见的 HTTP 时间格式。Request 的 PostFormValue 方法与 FormValue 类似,不过忽略了 URL 参数。CloseNotifier 接口提供了服务器端处理程序发现客户端断开连接的一种机制。ServeMux 类型现在有了 Handler 方法来访问 Handler 的路径而不需要执行它。Transport 现在可以通过 CancelRequest 取消一个正在进行的请求。***, 当 Response.Body 在完全被处理之前被关闭的话,Transport 现在会对关闭 TCP 连接保持更乐观的态度。

新的 net/http/cookiejar 包提供了基础的管理 HTTP cookie 的功能。

net/mail 包有了两个新函数,ParseAddress 和 ParseAddressList,来解析 RFC 5322 格式化的地址到 Address 结构体。

net/smtp 包的 Client 类型有了一个新的方法,Hello,用于向服务器发送 HELO 或 EHLO 消息。

net/textproto 有两个新函数,TrimBytes 和 TrimString,用来仅在 ASCII 下进行前后空符的切除。

新方法 os.FileMode.IsRegular 让了解一个文件是否是普通文件变得更加简单。

image/jpeg 现在可以读取预加载 JPEG 文件,并且处理某些二次取样配置信息。

regexp 包现在通过 Regexp.Longest 可以支持 Unix 原生的最左最长匹配,而 Regexp.Split 使用正则表达式定义的分离器将字符串分解成组的。

runtime/debug 有三个关于内存使用的新函数。FreeOSMemory 函数触发垃圾回收,并尝试将未使用的内存退回操作系统;ReadGCStats 获得控制器的统计信息;而 SetGCPercent 提供了一个可编程的途径来控制控制器执行频率,包括永远禁止其执行。

sort 包有一个新函数,Reverse。作为调用 sort.Sort 的参数的包裹,通过调用 Reverse 可以让排序结果反续。

strings 包有两个新函数,TrimPrefix 和 TrimSuffix 含义不言而喻,还有 Reader.WriteTo 方法,因此 Reader 现在实现了 io.WriterTo 接口。

syscall 包的有许多更新,包括对每个支持的操作系统的系统调用进行加固。

testing 包现在可以在性能测试中使用 AllocsPerRun 函数和 BenchmarkResult 的 AllocsPerOp 方法自动生成内存分配统计。还有 Verbose 函数来检测 -v 的命令行参数状态,和testing.B 和 testing.T 的新方法 Skip 来简单跳过一些不必要的测试。

在 text/template 和 html/template 包中,模板现在可以用圆括号来对字符序列分组,这简化了创建复杂的字符序列的过程。TODO:链接到一个实例。同时,作为新的解析器的一部分,Node 接口有两个方法用来提供更好的错误报告。这同样遵循 Go 1 兼容性规则,由于这个接口被明确期望只有text/template 和 html/template 包使用,而其安全机制保证了这点,所以应当没有代码会受到影响。

在 unicode/utf8 包中,新函数 ValidRune 报告了一个 rune 是否是一个合法的 Unicode 编码值。为了确保合法,rune 的值必须在范围内,且不能为半个代用符。

unicode 包的实现被更新到 Unicode 7.2.0 版本。

原文链接:http://www.mikespook.com/2013/03/%E7%BF%BB%E8%AF%91-go-1-1-%E4%BB%8B%E7%BB%8D/

责任编辑:张伟 来源: mikespook
相关推荐

2013-05-22 09:38:03

GoGo语言Go性能

2013-05-15 09:27:58

2013-05-15 09:37:00

GoGo1.1性能测试

2024-01-04 07:02:36

GoLangFiber开发

2022-06-02 13:54:04

Go数组切片

2019-02-11 08:32:22

编程语言Go

2013-05-15 09:51:03

GoGo1.1性能测试

2022-03-28 13:34:26

Go泛型部署泛型

2024-03-25 07:22:50

GolangMySQL数据库

2022-05-06 09:22:25

Go泛型

2013-07-11 09:29:24

J2EE7

2011-08-10 09:53:32

Python

2020-06-04 12:15:37

Go内存池对象池

2022-11-27 23:37:34

Go模式Workspaces

2009-01-14 16:07:18

JcoderJava IDE开发工具

2009-08-06 17:25:07

.NET框架基本要求

2018-09-10 15:44:21

UCloud树莓派

2011-09-06 11:32:13

UbuntuGnome Subti

2011-05-06 09:16:07

Qt SDK 1.1Qt SDKQt

2023-09-06 12:01:50

HTTP协议信息
点赞
收藏

51CTO技术栈公众号