暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

go1.17 新特性解读

零君聊软件 2021-08-23
1603

一如既往,go1.17继续满足go1.x兼容性原则,以前的源代码无需任何修改,就可以在go1.17上正确的编译和运行。


语言层面的改动

首先是slice可以直接转换成array pointer(数组指针)。直接来看一个例子:

    package main


    import (
    "fmt"
    )


    func main() {
    s := []int{6, 9, 4, 10, 15}
    fmt.Printf("len(s) = %d, cap(s) = %d\n", len(s), cap(s))
    a := (*[5]int)(s)
    fmt.Printf("len(a) = %d, cap(a) = %d\n", len(a), cap(a))


    fmt.Printf("type of s: %T, type of a: %T\n", s, a)


    for i := 0; i < 5; i++ {
        fmt.Printf("&s[%d] == &a[%d]: %t\n", i, i, &s[i] == &a[i])
      }
    }
    复制


    上面的例子是将一个类型为[]int的slice直接转换成类型为*[5]int的array pointer。输出结果为:

      len(s) = 5, cap(s) = 5
      len(a) = 5, cap(a) = 5
      type of s: []int, type of a: *[5]int
      &s[0] == &a[0]: true
      &s[1] == &a[1]: true
      &s[2] == &a[2]: true
      &s[3] == &a[3]: true
      &s[4] == &a[4]: true
      复制


      这里要注意一点,array的长度必须小于等于slice的长度,也就是len(s),否则会产生runtime panic。如果将上面的例子的第10行中的5改成6,则运行code时会产生如下panic:

        panic: runtime error: cannot convert slice with length 5 to pointer to array with length 6
        复制


        语言层面的另一个改动就是在unsafe包中增加了两个函数:Add和Slice。

          // The function Add adds len to ptr and returns the updated pointer
          // Pointer(uintptr(ptr) + uintptr(len)).
          // The len argument must be of integer type or an untyped constant.
          // A constant len argument must be representable by a value of type int;
          // if it is an untyped constant it is given type int.
          // The rules for valid uses of Pointer still apply.
          func Add(ptr Pointer, len IntegerType) Pointer


          // The function Slice returns a slice whose underlying array starts at ptr
          // and whose length and capacity are len.
          // Slice(ptr, len) is equivalent to
          //
          // (*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
          //
          // except that, as a special case, if ptr is nil and len is zero,
          // Slice returns nil.
          //
          // The len argument must be of integer type or an untyped constant.
          // A constant len argument must be non-negative and representable by a value of type int;
          // if it is an untyped constant it is given type int.
          // At run time, if len is negative, or if ptr is nil and len is not zero,
          // a run-time panic occurs.
          func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
          复制

          函数自带的注释已经说的很清楚了,这两个函数只是为了开发者更容易写出符合unsafe安全规则的代码。Add(ptr, len)等价于:

            Pointer(uintptr(ptr) + uintptr(len))
            复制

            Slice(ptr, len)等价于:

              (*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
              复制


              移植兼容性

              对于Darwin,Go 1.17要求 >= macOS 10.13。


              对于Windows,Go1.17增加了对64位ARM架构的支持,也支持cgo。

                windows/arm64
                复制


                对于OpenBSD,64位的MIPS架构开始支持cgo。另外,以前在Go 1.16中,只有64位的x86和ARM架构的系统调用是通过libc来完成;在Go1.17中,32位的x86和ARM架构的系统调用也是通过libc来完成的了。


                工具集

                这方面的变化其实挺多的,但是关键是Go module以及go:build这两个变化。

                Go module

                Go1.17中一个显著的改变就是支持修剪的模块依赖图(pruned module graph)。


                在以前的版本中,一个模块的依赖由其直接依赖与所有的间接依赖组成,也就是一个全集的依赖图;每个go.mod文件中只包含直接依赖。运行go命令的时候会解析当前go.mod以及每个依赖的go.mod文件,哪怕其实并不需要。


                在Go1.17中,go.mod文件只会包含该模块真正需要的依赖,也即是imports的包。但是会将其直接依赖和所有真正需要的间接依赖都包含在go.mod文件中。这样,会导致go.mod文件增大很多,所以对于间接依赖,会包含在一个单独的require块中。


                将一个现有的go.mod转换成Go1.17,可以运行下面的命令:

                  go mod tidy -go=1.17
                  复制

                  例如,针对etcd 3.5运行上面的命令,对go.mod的改变如下:


                  关于这部分的具体细节,参考:

                    https://tip.golang.org/ref/mod#graph-pruning
                    复制


                    //go:build

                    熟悉Golang交叉编译的同学应该对"// +build "这种语法并不陌生。其缺点是不直观,空格表示"或",逗号表示"与",不好记忆,也容易出错。例如,下面这个例子表示 (linux AND 386) OR darwin。

                      // +build linux,386 darwin
                      复制


                      在Go1.17中,开始推荐使用"//go:build"这种语法形式。上面的例子按照新语法就是:

                        // go:build linux && 386 || darwin
                        复制


                        当使用gofmt格式化源代码的时候,如果有"// +build"这种语法,gofmt会在其上面自动加上对应的"//go:build"语法形式。例如:

                          //go:build mips || mipsle
                          // +build mips mipsle
                          复制


                          关于"//go:build"的详细细节,参考:

                            https://go.googlesource.com/proposal/+/master/design/draft-gobuild.md
                            复制


                            编译器(Compiler)

                            以前传递函数参数以及返回值是通过栈,Go117的一个主要的变化就是支持通过寄存器传递函数参数以及返回值。直接的好处就是性能的提高(5%)以及二进制文件大小的减小(2%)。这个改变对程序员来说,一般是没有影响的;但是如果使用汇编或者unsafe包等方式来访问函数参数或者函数本身的地址,那可能会受到影响,毕竟传参的方式变化了,导致函数本身的地址偏移自然会随之变化。


                            另外一个变化是支持内联包含闭包的函数。这样就导致同一个函数可能会内联到不同的地方,从而其包含的闭包的地址也不同。例如下面的函数Demo包含闭包,当它被内联到不同的地方之后,其包含的内部函数的地址就会不同。当采用unsafe.Pointer的方式来比较其地址的时候,就可能不再相等。

                              func Demo() func() int {
                              a := 3
                              return func() int {
                              return a * 10
                              }
                              }
                              复制


                              Core Library

                              Core library的小变化挺多的,这里只提其中两个容易产生错误的地方。第一个改动就是net/url和net/http包不再支持分号(;)作为参数的分隔符。例如下面这个请求地址,在Go1.17之前的版本中会解析成三个参数;而在Go1.17中只会解析成一个参数c=3。

                                example?a=1;b=2&c=3
                                复制


                                另外一个值得注意的改动就是x509ignoreCN这个临时变量被删除了。这就意味着证书中如果还只有CN而没有SAN的时候,在Go1.17中就无法正常工作了。具体可以参考我以前对1.15以及1.16的新特性解读。

                                  GODEBUG=x509ignoreCN=0 
                                  复制


                                  总结

                                  对于大多数开发人员来说,重点需要关注的主要是以下几点:

                                  1. slice可以转换成array pointer;

                                  2. 对于pruned module graph的支持;

                                  3. 用"//go:build"取代"// +build";

                                  4. x509ignoreCN被彻底删除。

                                  --END--


                                  文章转载自零君聊软件,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                  评论