设为首页 加入收藏

TOP

golang1.13中重要的新特新(一)
2019-09-30 16:41:33 】 浏览:121
Tags:golang1.13 重要 新特

本文索引

golang1.13发布已经有一个月了,本文将会列举其中几个较为重要的特性。我们将会从语言变化、库变化以及工具链的改进这三方面逐个介绍新版本中引入的新特性。

语言变化

go团队一直承诺1.x版本的向前兼容,所以虽然1.13作为第一个开始向go2过渡的版本,其引入的语言变化是极少的,主要只有这两点:更多的数字字面量和改进的panic信息。

数字字面量

数字字面量是大家再熟悉不过的东西了,比如1000.991.等。

然而奇怪的是,1.13之前的golang仅支持10进制和16进制的字面量,而在其它语言中广泛支持的二进制和八进制却不受支持。例如下面的代码是无法编译的:

fmt.Println(0b101)
fmt.Println(0o10)

在go1.13中上述字面量语法已经被支持了你可以通过0b0B前缀来表明一个二进制数字的字面量,以及用0o0O来表明八进制字面量。值得注意的是虽然两种写法都可以,但是gofmt默认会全部转换为小写,所以我更推荐使用0b0o使你的代码风格尽量统一。

数字字面量的另一个变化就是引入了16进制浮点数的支持。

16进制浮点数是按照16进制来表示浮点数的方法,需要注意的是这里指的不是将浮点数表示为对应二进制值的16进制形式,而是形式如下的16进制数字:

0X十六进制整数部分.十六进制小数部分p指数

其中整数和小数部分和普通浮点字面量一样可以省略,省略的部分默认为0。p+指数的部分不可省略,指数可以有符号,它的值是2的指数。

一个16进制浮点字面量最终的结果,假设p之前的部分的值为a,p后的指数是b,最终的值如下:a * 2^b

看上去和科学计数法很像,事实上也就是把e换成了p,指数计算从10变为了2。另外因为是每16进1,所以0x0.1p0看上去像0.1,然而它表示的是1/16,而0x0.01p0则是1/16的1/16,初见会不太直观,但是习惯后就不会有什么问题了。举点例子:

二进制和八进制字面量是比较常用的,那16进制浮点数呢?答案是更高的精度和统一的表达。

0x0.1p0表示的十进制值是0.0625,而0x0.01p0是0.00390625,已经超过了float32的精度范围,所以16进制浮点字面量可以在有限的精度范围内表示更精确的数值。统一表达自然不用多解释,习惯16进制表达的开发者更乐于使用类似形式。

具体的示例可以参考这里

最后对于数字字面量还有一个小小的改进,那就是现在可以用下划线分隔数字增加可读性。举个例子:

fmt.Println(100000000)
fmt.Println(1_0000_0000)
fmt.Println(0xff_ff_ff)

分隔符可以出现在任意位置,但是像0x之类的算是一个完整的符号的中间不可以插入下划线,分隔符之间字符的数量没有规定必须相等,但为了可读性最好按照现有的习惯每3个数字或四个数字进行一次分隔。

越界索引报错的完善

虽然我将其归为语言变化,但事实上将其定义为运行时改进更为恰当。

众所周知golang对数组和slice的越界引用是0容忍的,一旦越界就会panic,例如下面的例子:

package main

import "fmt"

func main() {
        arr := [...]int{1,2,3,4,5}
        for i := 0; i <= len(arr); i++ {
                fmt.Println(arr[i])
        }
}

如果运行这个程序那么你会收到一个不短的抱怨:

这里的例子很简单,所以调用堆栈信息追溯起来不是很困难,可以方便得定位问题,但如果调用链较深或者你处于一个高并发程序之中,事情就变得麻烦了,要么依赖日志调试并最终分析排除大量杂音来定位问题,要么依赖断点进行单步调试,无论哪种都需要耗费大量的精力,而核心问题只是我们想直到为什么会越界,再浅一步,我们有时候或许只要知道导致越界的值就可以大致确定问题的原因,遗憾的是panic提供的信息中不包含上述内容,直到golang1.13。

现在golang会将导致越界的值打印出来,无疑是雪中送碳:

当然,panic信息再完善也不是灵丹妙药,完善的单元测试和严谨的工作态度才是bug最好的预防针。

工具链改进

语言层面的变动不是很大,但工具链就不一样了,除了去除了godoc程序,最大的变化仍旧集中在go modules上。

这次golang加入了三个环境变量来共同控制modules的行为,下面分别进行介绍。

GOPROXY

其实这个变量在1.12中就引入了,这次为其加上了默认值https://proxy.golang.org,direct,这是一个逗号分隔的列表,后面两个变量的值和它相同,其中direct表示不经过代理直接连接,如果设置为off,则进制下载任何package。

在go get等命令获取package时,会从左至右依次查找,如果都没有找到匹配的package,则会报错。

proxy的好处自然不用多说,它可以使国内开发者畅通无阻地访问某些国内环境无法获取的包。更重要的是默认的proxy是官方提供和维护的,比起第三方方案来说安全性有了更大的保障。

GOSUMDB

这个变量实际上相当于指定了一个由官方管理的在线的go.sum数据库。具体介绍之前我们先来看看golang是如何验证packages的:

  1. go get下载的package会根据go.mod文件和所有下载文件分别建立一个hash字符串,存储在go.sum文件中;
  2. 下载的package会被cache,每次编译或者手动go mod verify时会重新计算与go.sum中的值比较,出现不一致就会报安全错误。

这个机制是建立在本地的cache在整个开发生命周期中不会变动之上的(因为依赖库的版本很少会进行更新,除非出现重大安全问题),上述机制可以避免他人误更新依赖或是本地的恶意篡改,然而现在更多的安全问题是发生在远程环境的,因此这一机制有很大的安全隐患。

好在加入了GOSUMDB,它的默认值为“sum.golang.org”,国内部分地区无法访问,可以改为“sum.golang.google.cn”。现在的工作机制是这样的:

  1. go get下载包并计算校验和,计算好后会先检查是否已经出现在go.sum文件中,如果没有则去GOSUMDB中检查,校验和一致则写入go.sum文件;否则报错
  2. 如果对应版本的包的校验和已经在go.sum中,则不会请求GOSUMDB,其余步骤和旧机制一样。

安全性得到了增强。

GOPRIVATE

最后要介绍的是GOPRIVATE,默认为空,你可以在其中使用类似Linux glob通配符的语法来指定某些或某一类包不从proxy下载,比如某些rpc套件自动生成的package,这些在proxy中并不会存在,而且即使上传上去也没有意义,因此你需要把它写入GOPRIVATE中。

还有一个与其类似的环境变量叫GONOPROXY,值的形式一样,作用也基本一样,不过它会覆盖GOPRI

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇GoLang 开山篇 下一篇[系列] go-gin-api 路由中间件 - ..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目