写这篇博客缘由,因为我在网上搜了很多篇关于 Go 1.13 的文章,但是很多是直接翻译官方博客介绍的,对我们Go开发经验不是很长的人来说,因为没有对应大量实战经验,看了比较蒙,理解不了,就是感觉看不懂为什么需要加这些新特性,所以就自己搜集了很多相关文章,消化理解之后写出了这篇博客。Go 1.0发布到 1.13版本,Go语言本身的语法改动非常少。
一、内容
1)Tools 变动
2)标准库 变动
3)语言 spec 方面变动
4)跨平台支持
二、Tools
-
- 升级到 Go 1.13 后 GOPROXY 和 GOSUMDB 都会有默认值,且默认值在国内是无法访问的,所以为了一切正常,建议大家
go env -w GOPROXY=https://goproxy.cn,direct
,这个命令是 Go 1.13 新加的,然后 GOSUMDB 就不用改了,因为 goproxy.cn 支持代理它的默认值,所以直接就能用。在 Go1.13 之前,GOPROXY=https://goproxy.cn 即可,逗号列表是 Go1.13 才有的。 - 如果你用了 GOPROXY 或 GOSUMDB,那么你就可能需要了解一下 GONOPROXY、GONOSUDB 还有 GOPRIVATE。前两个是指定 Go 该怎么处理模块的下载与校验;后三个是指定 Go 在那些情况下不应该根据前两个处理。所以,私有库的问题可以解决。
- 关于 GOPROXY,看看 goproxy.cn 的作者发布的文章:goproxy.cn - 为中国 Go 语言开发者量身打造的模块代理。
- 升级到 Go 1.13 后 GOPROXY 和 GOSUMDB 都会有默认值,且默认值在国内是无法访问的,所以为了一切正常,建议大家
-
Command
- go env 支持 -w 参数为用户单独设置环境变量,-u 参数取消设置
- go version 可以查看二进制文件的编译 go 版本
- go build 的参数 trimpath 可以二进制文件中的文件系统路径以提高 build 效率;-tags 支持多个编译选项
- go generate 增加 generate build 选项
Compiler Toolchain
主要改进在逃逸分析上更加准确。因为函数栈帧上的变量会随着函数调用结束而释放,所以这可能会导致之前运行 OK 的程序出错(比如使用 unsafe 包导致之前误诊断,这也是一个不要使用 unsafe 包的原因)。如果要保持之前的行为可以设置变量:go build -gcflags=all=-newescape=false
Assembly
支持 ARM v8.1 引入的原子指令Gofmt
支持格式化前面说到的数字格式。比如:0B1010
,0XabcDEF
,0O660
,1.2E3
,and01i
经过 fmt 之后得到0b1010
,0xabcDEF
,0o660
,1.2e3
,and1i
。-
godoc
从主包中移除,如果要使用,需要单独安装go get golang.org/x/tools/cmd/godoc godoc
Runtime
Out of range 的 panic 信息会包含 index 和 cap 的信息。 Defer 的性能提升 30%,defer的性能消耗可以参考:尼不要逗了:深入理解 Go 语言 defer,Runtime 归还给 OS 的内存策略会更激进(原文是 aggressive,我理解这里的激进的意思大概是有空闲的就还给你,相比之前会保留一段时间)。但是由于很多操作系统对内存的回收都是惰性的,所以 Runtime 这个地方的改变可能并不会导致进程的实际使用的物理内存显著减少,除非系统内存不足,主动回收。
三、标准库 变动
1)errors包(Error Wraping,包装错误)
支持 wrapping,fmt.Errorf 增加 %w 格式符,errors 包增加三个函数(Unwrap、Is、As
)
-
errors.Unwrap
函数,原型func Unwrap(err error) error
,功能:- 对 err 进行断言,看它是否实现了 Unwrap 方法,如果是,调用它的 Unwrap 方法,否则,返回 nil。源码如下
func Unwrap(err error) error { // 判断 err 是否实现 Unwrap 函数,也就是是否 是 wrapping error u, ok := err.(interface { Unwrap() error }) // 如果不是,返回nil if !ok { return nil } // 否则则调用该 error 值的Unwrap方法返回被嵌套的error return u.Unwrap() }
errors.Is
函数,原型func Is(err, target error) bool
,功能:
- 如果
err
和target
是同一个,那么返回true
- 如果 err 有实现 Is 比较方法,则使用 err的Is 方法来比较,如果err的Is返回为true,则返回true,否则继续 error.Is 的比较
- 如果
err
是一个wrap error,target
也包含在这个嵌套error链中的话,那么也返回true
。
很简单的一个函数,要么咱俩相等,要么err
包含target
,这两种情况都返回true
,其余返回false
。源码如下
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
// for循环,把 err 错误链一层层通过剥开,一个个比较,找到就返回true
for {
if isComparable && err == target {
return true
}
// 这里意味着你可以自定义 error 的Is方法,实现自己的比较代码
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
// 剥开一层,返回被嵌套的err
if err = Unwrap(err); err == nil {
return false
}
}
}
}
为什么需要 Is 函数?
在 Go 1.13 之前没有 wapping error 的时候,我们判断 error 是不是同一个 error 可以使用如下方法:
if err == os.ErrExist
这样我们就可以通过判断来做一些事情。但是有了 Is 方法后,我们可以进行更复杂的比较了
-
errors.As
函数,原型func As(err error, target interface{}) bool
,功能:
- 如果 target 为 nil 或者 nil 指针,或者 不为接口 且 没有实现 error接口 的话,会 panic
- 从 err 错误链里找到和 target 相等的并设置 target 所指向的变量,返回 true,否则返回 false
func As(err error, target interface{}) bool {
// 一些判断,保证 target ,这里是不能为 nil
if target == nil {
panic("errors: target cannot be nil")
}
val := reflectlite.ValueOf(target)
typ := val.Type()
// 这里确保 target 必须是一个非 nil 指针
if typ.Kind() != reflectlite.Ptr || val.IsNil() {
panic("errors: target must be a non-nil pointer")
}
// 这里确保 target 是一个接口或者实现了 erorr 接口
if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) {
panic("errors: *target must be interface or implement error")
}
targetType := typ.Elem()
for err != nil {
// 关键部分,反射判断是否可以被赋予,如果可以赋予并且返回 true
if reflectlite.TypeOf(err).AssignableTo(targetType) {
val.Elem().Set(reflectlite.ValueOf(err))
return true
}
// 这里意味着你可以自定义 error 的As方法,实现自己的类型断言代码
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
err = Unwrap(err)
}
return false
}
剩下一个 %w 是什么呢?打开源码
// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand. It is
// invalid to include more than one %w verb or to supply it with an operand
// that does not implement the error interface. The %w verb is otherwise
// a synonym for %v.
// Errorf根据格式说明符设置格式,并以
//满足错误的值。
//
//如果格式说明符包含带有错误操作数的%w动词,
//返回的错误将实现Unwrap方法,返回操作数。它是
//包含多个%w动词或为其提供操作数无效
//没有实现错误接口的代码。 %w动词否则为
//%v的同义词。
func Errorf(format string, a ...interface{}) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
if p.wrappedErr == nil {
err = errors.New(s)
} else {
err = &wrapError{s, p.wrappedErr}
}
p.free()
return err
}
type wrapError struct {
msg string
err error
}
func (e *wrapError) Error() string {
return e.msg
}
func (e *wrapError) Unwrap() error {
return e.err
}
从源码中我们可以得知是这样的,传入对应 %w 的值是一个实现了 error 接口的值,则返回一个带 实现了Unwrap的error值,否则 %w 的效果等于 %v。
2)crypto/ed25519 包
package crypto/ed25519 实现了之前在 package golang.org/x/crypto/ed25519 中实现的Ed25519 算法。
3)reflect包
reflect
标准库包中将为Value
类型添加一个IsZero
方法,用来判断一个值是否为零值。
四、语言 spec 方面改动
主要集中在这个 Proposal:golang/proposal,主要是一些数字的表示方面,包括:
- 使用 0b/0B 开头来表示二进制数(赋值给变量之后,会被转换程十进制,还是int、float等类型)
- 使用 0o/0O(前面是数字零,后面是字母o)表示八进制数(如上)
- 使用0x/0X 开头来表征十六进制数(如上)
- 使用 i 来表示复数
- 可使用下划线 _ 来作为数字分割
右移位操作的 unsign 限制移除,Proposal: golang/proposal,移位操作中的右移位将允许为有符号整数,在1.13之前的Go 版本,<< 右边必须为无符号类型确定整数或者可以表示成uint
值的类型不确定值,在1.13之后,加上了有符号整数的非负值,如果是负正数,将panic
五、库平台支持
- AIX:aix平台支持 cgo
- Android:兼容 Android Q
- Darwin:Go 1.13 支持需要macOS 版本 10.11 El Capition 及以上
- FreeBSD:FreeBSD 11.2及以上
- Illumos:编译选项
GOOS==illumos
支持 - NetBSD:支持 NetBSD on ARM64
- OpenBSD:支持 OpenBSD on ARM64
- Windows:底层要求 Windows 7
视频资源
- 【Go 夜读】#58 What's new in Go 1.13?,可能是我水平不够,听着很蒙感觉