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

Go 1.16新特性解读

零君聊软件 2021-02-17
1987

Go 1.16于今天凌晨(北京时间)发布了,基本还是半年一个release的节奏。总的来说,新特性很少,基本都是一些改进。


Go 1.16同样遵守了Go 1.x兼容性承诺。也就是以前的程序依然不用任何改动,就可以正确地在Go1.16上编译并运行。


语言层面改动

语言层面的改动,只指程序员在写代码的时候能感知到的变化。这里要明确说明一下,Go 1.16在语言层面没有任何改动。


这里简单回顾一下Go 1.15和Go 1.14在语言层面的改动。Go 1.15在语言层面同样也没有任何改变。Go 1.14主要增加对钻石型(菱形)接口嵌套结构的支持。具体请参考:

Go 1.15新特性解读

golang1.14新特性解读


Ports(移植兼容性)

Darwin and iOS

Go 1.16增加对64位ARM架构+macOS的支持(也称为Apple Silicon),跨平台build的时候如下设置GOOS与GOARCH:

    GOOS=darwin GOARCH=arm64


    对于iOS的支持,以前是darwin/arm64,现在更名为ios/arm64,所以跨平台build的时候如下设置GOOS与GOARCH:

      GOOS=ios GOARCH=arm64
      # 如果linux平台,则如下设置
      GOOS=android GOARCH=arm64


      Go 1.16也增加了对ios/amd64的支持,主要是为了在AMD64 macOS上运行iOS模拟器。


      Go 1.16是最后一个支持mac 10.12 (Sierra)的版本。在Go 1.17中将要求macOS 10.13 (High Sierra)或者更新的版本。


      NetBSD

      在NetBSD上增加了对64位ARM的支持。

        GOOS=netbsd GOARCH=arm64


        OpenBSD

        现在支持MIPS64。不过目前还不支持cgo。

          GOOS=openbsd GOARCH=mips64


          值得注意的一点是,在OpenBSD上,对于64位x86和64位ARM是通过libc进行系统调用,而不是直接通过SYSCALL/SVC指令了。这主要是为了与将来的OpenBSD版本保持向前兼容。在OpenBSD 6.9之后,要求通过libc进行系统调用。


          386

          Go 1.16不再支持x87模式(GO386=387)编译了。对于非SSE2处理器的支持可以通过软浮点模式(GO386=softfloat)来实现。


          RISC-V

          对于linux/risv64,Go 1.16现在支持cgo以及-buildmode=pie。Go 1.16也包含了一些针对RISC-V的性能优化。


          工具集

          Go command

          不管当前工作目录或者父目录是否包含go.mod文件,都会默认打开(enable) Module模式。也就是说,环境变量GO111MODULE默认是设置成on;如果要继续保持以前版本的行为,则需要手动将其设置成auto。


          在以前的版本中,运行任何go命令都会自动更新go.mod和go.sum。在Go 1.16中,“go build”和“go test”不会再自动更新go.mod和go.sum了。如果它们发现需要更新这两个文件,就会报错。


          但是"go get"和“go tidy"仍会自动更新go.mod和go.sum。对于"go get"会自动更新go.mod和go.sum,社区进行了大量的讨论,普遍认为不应该自动更新这两个文件。具体可参考:

            https://github.com/golang/go/issues/27643
            https://github.com/golang/go/issues/30515
            https://github.com/golang/go/issues/40276



            为了解决这个问题,应该使用"go install";也就是当需要build并install一个包的时候,应该使用"go install",而不是"go get"。

              https://go-review.googlesource.com/c/proposal/+/243077


              "go install"可以指定特定的版本,例如:

                go install example.com/cmd@v1.0.0


                对于"go get",正确的使用姿势应该是带上选项“-d”,只用来更新依赖项,而不build包。在将来的版本中,“-d”将会始终被enable。


                另外,可以在go.mod文件中使用retract指令来废弃一个特定的版本。以前的行为是使用比废弃版本高的下一个版本。


                内嵌文件

                在Go 1.16中增加了一个新的指令//go:embed,来支持内嵌静态文件。关于这一点,我以前的一篇文章中有介绍,请参考:

                记录被go历史车轮碾压的几个开源项目


                go test

                当运行"go test"时,如果一个测试函数中调用了os.Exit(0),那么测试函数被认为是失败了。但是在TestMain中调用os.Exit(0),仍然被认为是通过了测试。


                go get

                在"go get"中使用 "-insecure"已经是deprecated了,在将来的版本中会被删除。如果还想继续使用不安全的协议(比如HTTP),那就使用环境变量GOINSECURE。如果想绕过module sum的校验,那就使用环境变量GOPRIVATE或者GONOSUMDB。


                环境变量GOVCS

                GO 1.16中增加了一个新的环境变量GOVCS,用来限制使用哪个版本控制工具来下载源代码。


                Cgo

                cgo工具不再将C语言里的位域字段翻译成Go语言里结构体中的字段。


                Vet

                Vet新增了几条告警。首先,如果在测试函数创建的goroutine中调用Fatal/Fatalf/FailNow/Skip/Skipf/SkipNow等方法,则vet会产生warning。调用这些方法会导致测试函数创建的gorouting停止执行,但是不会影响测试函数自身。推荐的做法是使用t.Error产生错误,并用其它方法退出goroutine,例如return。


                当使用汇编指令操作BP寄存器(frame pointer),但没有保存并恢复该寄存器时,Vet也会产生warning。


                当传递非指针参数或者nil给asn1.Unmarchal时,vet也会产生warning。


                Runtime

                首先,引入了新的包runtime/metrics,来取代runtime/ReadMemStats和debug.GCStats。


                其次,设置GODEBU为inittrace=1,那么runtime会将每一个package中的init函数的执行时间和内存分配等信息输出到stderr。


                在Linux平台上,runtime现在默认是立即释放(MADV_DONTNEED)不需要的内存,而不是拖延到OS遇到memory pressure(MADV_FREE)的时候才释放。所以,对于进程的内存统计信息会更加准确。对于目前在使用该选项(如下)的系统,在Go 1.16中已经不需要设置该选项了,因为默认就是enable了,

                  GODEBUG=madvdontneed=1


                  Compiler

                  编译器现在可以内联(inline)不带label的for循环、方法值以及type switch的函数了。也包含了其它方面的内联优化。


                  Linker

                  在GO 1.16中,linker做了很大的改进。首先是减少了资源的使用,包括时间和内存方面。也提高了代码的健壮性和可维护性。


                  在Go 1.15中,对linker的性能优化主要集中在ELF-based OS以及amd64上;而在Go 1.16中,对linker的性能优化在涵盖了所有支持的OS/ARCH组合。而且比Go 1.15的链接速度更快,需要的内存更少。


                  Core library

                  内嵌文件

                  前面已经提到过,在编译期间可以通过新的指令//go:embed来内嵌静态文件。新增的包embed,就是用来访问这些内嵌的文件。


                  文件系统

                  引入了新包io/fs,其中定义了接口fs.FS,用来处理只读的文件树(read-only trees of files)。embed.FS、zip.Reader以及os.DirFS均实现了fs.FS。


                  函数http.FS将一个fs.FS实例转化成一个http.FileSystem实例。html/template与text/template中的函数ParseFS从一个fs.FS实例中读取模版。


                  对于实现了fs.FS的测试代码,新包testing/fstest提供了一个函数TestFS用来检查具体的实现。另外,也提供了一个简单的基于内存的文件系统的实现MapFS。


                  Deprecation of io/ioutil

                  包io/ioutil已经被deprecated了。io/ioutil中的"函数/方法/类型"应该用包io以及os中的相应对象替代,具体见下表:

                  io/ioutil中的"对象"(deprecated)
                  io或os中的"对象"(new)
                  Discard
                  io.Discard
                  NopCloser
                  io.NopCloser
                  ReadAll
                  io.ReadAll
                  ReadDir
                  os.ReadDir
                  ReadFile
                  os.ReadFile
                  TempDir
                  os.MkdirTemp
                  TempFile
                  os.CreateTemp
                  WriteFile
                  os.WriteFile


                  Minor changes to the libary

                  Libray中小改动还是挺多的,这里只是选取几个简要说一下。


                  archive/zip中的方法Reader.Open实现了fs.FS接口。


                  包crypto/x509的改动。选项"GODEBUG=x509ignoreCN=0"将在Go 1.17中被删除。如果对于Go 1.15中的变动了解的话,就知道这个改动意味着什么了。在Go 1.14以及以前的版本中,当X.509证书中没有Subject Alternative Name时,就会使用CommonName作为hostname。而在Go 1.15中,CommonName默认是被deprecated的。如果还想继续使用CommonName,则可以给环境变量GODEBUG增加"x509ignoreCN=0"。在Go 1.16中可以继续使用该选项,但是在Go 1.17中将会被删除。另外,值得注意的是,ParseCertificate与CreateCertificate开始强制对DNSNames、EmailAddress以及URIs字段实行字符串编码限制,这些字段只能包含ASCII范围内的字符。


                  包encoding/asn1的改动。当传递给Unmarshal与UnmarshalWithParams的参数不是指针或者是nil时,会返回一个error,而不是panic了。


                  对Unicode的支持从12.0.0升级到了13.0.0,增加了5930个新的字符。


                  总结

                  总的来说,Go 1.16中的新特性比较少,大部分都是一些改进和优化,在本文开头也提到了。


                  对于绝大多数开发人员来说,最值得关注新特性应该还是对内嵌文件的支持。一个相关的关注点就是io/ioutil被deprecated,要用包io与os中相应的对象来替换。


                  另外,如果单纯只是想安装一个包,应该使用"go install",而不是"go get"。


                  最后,要提一下“GODEBUG=x509ignoreCN=0”,这个选项在Go 1.17中会被删除。因为去年下半年在用户的生产环境中,遇到过这个问题,用户的证书里面没有用SAN,而是用的CN,而我们一个团队盲目升级到了Go 1.15,结果就出问题了,临时的解决方案就是增加这个runtime选项。


                  --END--

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

                  评论