Go1到Go1.10的语法变迁

栏目: Go · 发布时间: 5年前

Go1到 <a href='https://www.codercto.com/topics/6127.html'>Go</a> 1.10的语法变迁

因为Go1承诺,Go1后序的版本都保持了向前兼容的目标。不过在从Go1发展到Go1.10的过程中,语言依然是增加了一些新的特性。我们简单回顾Go1到Go1.10的变化。

1. Go1.2(2013年12月)

Go1.2最大的语言变化是切片操作时,可以设置新切片的容量。这个需求在Go1之前就被提出了,但是因为Go1修改工作较大而延期到了Go1.2才被实现。

比如下面的代码:

1var a = make([]int, 10)
2
3var b = a[i:j:k]

其中 b 切片是从 a 切片的第 i 个元素开始到第 j 个元素前结束, b 切片的容量为 kk 指定的容量不能超出 a 切片的容量)。

为了配合切片语法的变更, reflect 包也增加了相应的方法:

1func (v Value) SetCap(cap int)
2
3func (v Value) Slice3(low, high, max int) Value
4

其中 Value.SetCap 只调整切片的容量,和 a[::cap] 写法效果等价。而 Value.Slice3 在进行切片操作的同时也指定新切片的容量,和 a[low:high:max] 写法效果等价。

通过限制子切片的容量,可以将不同子切片进行安全的分割,避免子切片无意越界操作其它切片空间。

2. Go1.4(2014年12月)

Go1.4语言部分对 for 语法进行了加强。在Go1.3之前 for 只有下面两种写法:

1for i, v := range x {
2    // ...
3}
4
5for i := range x {
6    // ...
7}

for range 针对要循环变量类型的不同,产生的循环变量也有差异。在第一种写法中,如果要循环的是数组或切片类型则 iv 分别表示索引的下标和元素的值,如果循环的类型是map类型时则 iv 分别表示键和值,这种写法不能用于管道类型变量的迭代。而第二种循环也可以用管道变量的迭代,直到管道被关闭时结束。如果用第二种方式循环遍历数组或map,则和 for i, _ := range x {} 的写法相关相同,相当于忽略的要迭代的值。

但是有时候我们仅仅是要循环几次而并不关心循环变量的值,在Go1.3之前可以这样写:

1var times [5][0]int
2for i := 0; i < len(times); i++ {
3    // ...
4}
5
6for _ = range times {
7    // ...
8}

前一种方式采用传统的 for 循环方式遍历,而后一种方式采用 for range 遍历,但是获取了每次遍历到的值。

在Go1.4中,后一种方式可以省略掉前面的垃圾桶变量,像这样写:

1var times [5][0]int
2
3for range times {
4    // ...
5}

其中 times 对应一个 [5][0]int 类型的数组,虽然第一维数组有长度,但是数组的元素 [0]int大小0 ,因此整个数组占用的内存大小依然是 0 。没有付出额外的内存代价,我们就通过 for range 方式实现了 times 次快速迭代。

3. Go1.7(2016年8月)

在Go1.3的时代(2014年),Go语言官方博客专门属文引入了 context 概念包,并稍后在 golang.org/x/net/context 提供了官方的实现。 context 包是Go语言官方对Go进行并发编程的实践成果,用来简化对于处理单个请求的多个Goroutine之间与请求域的数据、超时和退出等操作。 context 包推出后就被社区快速吸收使用,例如gRPC以及很多Web框架都通过 context 来控制Goroutine的生命周期。

在Go1.7发布时,作为扩展包的 golang.org/x/net/context 终于移到标准库中。Go语言官方博客已经有专文讲述了 context 包的使用,这里就不详细展开了。感兴趣的读者可以查看并发相关的文档和书籍。

4. Go1.8(2017年2月)

Go1.8语言有一个小的变化,如果两个结构体成员名字和底层的类型相同(忽略成员的标签字符串差异),那么结构体底层对应相同的结构可以相互强制转型。

比如下面的代码:

 1func main() {
 2    type T1 struct {
 3        X int `json:"foo"`
 4    }
 5
 6    type T2 struct {
 7        X int `json:"bar"`
 8    }
 9
10    var v1 = T1{X: 9527}
11    var v2 = T2(v1) // now legal
12
13    fmt.Println(v2)
14}

T1T2 仅仅是成员标签字符串不同,但是底层结构是相同的,它们可以相互强制转型。

5. Go1.9(2017年8月)

Go1.9终于引入了类型别名的特性。类型别名的特性如下:

1type T1 = T2

类型别名 T1 是通过 = 符号从 T2 定义,这里的 T1T2 是完全相同的类型。

Go语言的接口是一大亮点特性,接口是方法的集合,而方法正是依附于类型的函数。而类型别名的一个特殊的地方是, T1 并不是一个新的类型,因此我们不能再为 T1 定义任何新的方法。

之所以引入类型别名,很大的原因是为了解决Go1.7将 context 扩展库移动到标准库带来的问题。因为标准库和扩展库中分别定义了 context.Context 类型,而不同包中的类型是不相容的。而gRPC等很多开源的库使用的是最开始以来的扩展库中的 context.Context 类型,结果导致其无法和Go1.7标准库中的 context.Context 类型兼容。这个问题最终通过类型别名解决了:扩展库中的 context.Context 类型是标准库中 context.Context 的别名类型,从而实现了和标准库的兼容。

类型别名虽然是为了解决特定问题而引入的补丁特性。但是从类型别名我们可以发现一些有趣的用法:

1type ReaderA interface {
2    Read(p []byte) (n int, err error)
3}
4
5type ReaderB = interface {
6    Read(p []byte) (n int, err error)
7}

上面定义的两个读接口都有同样的方法集合。而Go语言的接口是采用隐式的转义,因此可能有人会觉得这两种写法根本没有什么意义!

但是接口本身也是一种类型,如果我们基于 ReaderAReaderB 类型继续构造新的方法,就产生了差异:

1type MakeReaderA interface {
2    MakeReader() ReaderA
3}
4
5type MakeReaderB interface {
6    MakeReader() ReaderB
7}

虽然接口定义方法的名字相同,但是方法返回的是两种不同的类型,因此方法的签名是不同的,所以说上面的两个接口并不相同。

我们现在考虑通过类型别名的方式定义一个 ReaderC 接口,然后定义一个 MakeReaderC 接口:

1type ReaderC = interface {
2    Read(p []byte) (n int, err error)
3}
4
5type MakeReaderC interface {
6    MakeReader() ReaderC
7}

比较神奇的是 MakeReaderCMakeReaderB 接口可能是等价的,因为它定义的方法名和签名都是相同的。MakeReader方法返回的都是一个匿名的 interface { Read(p []byte) (n int, err error) } 接口类型。而Go语言中,所有的结构相同的匿名类型其实是同一个类型。

如果通过类型别名从匿名接口构造接口,就可以避免新定义的不同接口类型对接口的方法签名造成影响。

6. 总结

Go1.10可以说是Go1和Go2的精神分隔点. Go语言官方团队从Go1.10开始严肃思考对泛型等重大特性的设计. 为了解决Go1的遗留问题, 为Go2轻装上路准备, Go官方从Go1.11开始实现了模块机制. 模块机制将在Go1.13版本正式转正, 以后的版本将彻底禁止基于GOPATH和vendor包版本管理特性. 因此回顾Go1到Go1.10语言的演化历史, 可以帮助我们更好地理解Go2发展的方向.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序设计方法(中文版)

程序设计方法(中文版)

Matthias Fellisen / 黄林鹏、朱崇恺 / 人民邮电出版社 / 2003-12 / 49.00元

《程序设计方法》以Scheme语言为基础介绍计算和程序设计的一般理论和实践。《程序设计方法》由8个部分和7个独立的章节(第8、13、18、24、29、33、38章)组成。8个部分主要讨论程序设计,独立章节则介绍一些与程序设计和计算相关的话题。《程序设计方法》第1至第3部分介绍了基于数据驱动的程序设计基础。第4部分介绍了程序设计中的抽象问题。第5部分和第6部分是与递归及累积相关的内容。《程序设计方法......一起来看看 《程序设计方法(中文版)》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换