#1 Go1.13的新特性

写这篇博客缘由,因为我在网上搜了很多篇关于 Go 1.13 的文章,但是很多是直接翻译官方博客介绍的,对我们Go开发经验不是很长的人来说,因为没有对应大量实战经验,看了比较蒙,理解不了,就是感觉看不懂为什么需要加这些新特性,所以就自己搜集了很多相关文章,消化理解之后写出了这篇博客。Go 1.0发布到 1.13版本,Go语言本身的语法改动非常少。

一、内容

1)Tools 变动
2)标准库 变动
3)语言 spec 方面变动
4)跨平台支持

二、Tools

  1. Module

    • 升级到 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 语言开发者量身打造的模块代理
  2. Command

    • go env 支持 -w 参数为用户单独设置环境变量,-u 参数取消设置
    • go version 可以查看二进制文件的编译 go 版本
    • go build 的参数 trimpath 可以二进制文件中的文件系统路径以提高 build 效率;-tags 支持多个编译选项
    • go generate 增加 generate build 选项
  3. Compiler Toolchain
    主要改进在逃逸分析上更加准确。因为函数栈帧上的变量会随着函数调用结束而释放,所以这可能会导致之前运行 OK 的程序出错(比如使用 unsafe 包导致之前误诊断,这也是一个不要使用 unsafe 包的原因)。如果要保持之前的行为可以设置变量:go build -gcflags=all=-newescape=false

  4. Assembly
    支持 ARM v8.1 引入的原子指令

  5. Gofmt
    支持格式化前面说到的数字格式。比如:0B10100XabcDEF0O6601.2E3,and 01i 经过 fmt 之后得到 0b10100xabcDEF0o6601.2e3,and 1i

  6. godoc
    从主包中移除,如果要使用,需要单独安装

    go get golang.org/x/tools/cmd/godoc
    godoc
    
  7. Runtime
    Out of range 的 panic 信息会包含 index 和 cap 的信息。 Defer 的性能提升 30%,defer的性能消耗可以参考:尼不要逗了:深入理解 Go 语言 deferRuntime 归还给 OS 的内存策略会更激进(原文是 aggressive,我理解这里的激进的意思大概是有空闲的就还给你,相比之前会保留一段时间)。但是由于很多操作系统对内存的回收都是惰性的,所以 Runtime 这个地方的改变可能并不会导致进程的实际使用的物理内存显著减少,除非系统内存不足,主动回收。

三、标准库 变动

1)errors包(Error Wraping,包装错误)
支持 wrapping,fmt.Errorf 增加 %w 格式符,errors 包增加三个函数(Unwrap、Is、As

  1. 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()
    }
    
  2. errors.Is函数,原型 func Is(err, target error) bool,功能:

  • 如果errtarget是同一个,那么返回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 方法后,我们可以进行更复杂的比较了

  1. 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

视频资源

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345