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发展的方向.


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

查看所有标签

猜你喜欢:

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

Programming Concurrency on the JVM

Programming Concurrency on the JVM

Venkat Subramaniam / The Pragmatic Bookshelf / 2011-6-1 / USD 35.00

Concurrency on the Java platform has evolved, from the synchronization model of JDK to software transactional memory (STM) and actor-based concurrency. This book is the first to show you all these con......一起来看看 《Programming Concurrency on the JVM》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具