Golang 1.17版本发布,新功能抢先体验

发表时间: 2021-08-18 08:30

今日Golang官方网站宣布发布最新1.17。本地版本发布距离上一个稳定版本1.16过去了六个月了,新版本坚持Golang 1的版本兼容承诺不会影响绝对多数的Golang应用。该发布主要在工具链、运行时和库更新。 详细的功能介绍请随虫虫一起学习。

语言变化

Golang1.17 在语言方面带了三个小的改进。

从切片转化到数组指针:在新版本中,一个[]T类型表达式s的可以转换为数组指针类型 *[N]T。对于转换结果数组a,其索引范围需要满足条件:

&a[i] == &s[i],0 <= i < N

如果 len(s)小于N则程序会panic

unsafe.Add:unsafe.Add(ptr, len)添加len到ptr并返回更新后的指针 unsafe.Pointer(uintptr(ptr) + uintptr(len)).

unsafe.Slice:对T*类型ptr表达式unsafe.Slice(ptr, len)函数返回一个[]T类型的切片。其数组下表以ptr开始,程度为len。

添加了包不安全增强功能以简化编写符合到unsafe.Pointer的安全规则 ,但规则保持不变。特别是现有的正确使用的程序unsafe.Pointer还有效,并且新程序必须遵守规则使用unsafe.Add或unsafe.Slice.

注意,从切片到数组指针的新转换是第一种类型转换,可能在运行时发生panic情形。

Ports

Darwin

Golang支持macOS,需要的版本为10.13 Sierra 或更高版本;

Windows

Windows 平台Go 1.17 增加了ARM 64位架构的支持(windows/arm64 ports),支持cgo。

OpenBSD

OpenBSD 上的64位MIPS架构(openbsd/mips64)新版本中支持cgo。

在Go 1.16中,在64位x86和64位ARM架构上OpenBSD( penbsd/amd64和 openbsd/arm64)系统调用是通过libc。Go 1.17中,在OpenBSD 上的32位x86和32位ARM架构上也是同样调用(openbsd/386和 openbsd/arm端)。这确保了与OpenBSD 6.9以后的兼容性,后者需要要通过的系统调用libc对于非静态 golang二进制。

ARM64

Golang程序新版中在64位ARM上维护堆栈帧指针,对所有操作系统上都是同样的架构。之前的堆栈帧指针仅适用于Linux、macOS和iOS。

loong64 GOARCH 值保留

主要的Golang编译器还不支持 LoongArch 架构,保留GOARCH值“loong64”。 Go文件名为*_loong64.go现在会会被 Go工具忽略,除非正在使用该GOARCH值。

工具链

Go命令

模块图精炼

如果模块指定go 1.17或者之后的版本。模块图仅包括直接依赖的go 1.17模块,而非其完整传递依赖关系。

为了保持go命令可以正确解析精简的模块依赖关系图,每个模块的go.mod文件需要包括有关与该模块相关的传递依赖项的更多详细信息。如果一个模块go.mod文件go版本为1.17,其go.mod文件需要用require指令明确指定每个提供可传递导入包的模块。

go.mod模块图精简所需的文件包括加载任何包的导入所需的所有依赖项主模块,如果主模块指定为go 1.17或之后版本,go工具不再读取或下载,其请求中不必要的主go.mod中的模块。

由于显式的扩展一个扩大了Go 1.17的go.mod文件的,go 1.17中新增的间接的依赖需要单独在require块维护。

为了方便升级到 Go 1.17精简模块图,go mod tidy子命令现在支持 -go选项用来转换现有模块的go.mod文件的go版本到Go 1.17。可以运行:

go mod tidy -go=1.17

默认情况下,go mod tidy验证与主模块相关的所选依赖项版本相同之前的Go版本使用的版本,并保留go.sum版本所需的条目。

-compat标志允许覆盖该版本以支持较旧的版本,直到指定的版本go指令中的 go.mod文件。

go mod tidy -compat=1.17

请注意,即使主模块使用-compat=1.17进行转换, go 1.16或更早的模块的用户require模块来仍然可以使用,前提是包只使用兼容的语言和库特征。

go mod graph子命令也支持-go标志,这会使得报告所指示的Go版本所看到的图表显示可能存在的依赖关系,否则会被精简。

模块弃用评论

模块作者可以go.mod中添加// Deprecated: comment弃用一个模块,然后标记新版本。go get如果模块需要,现在打印警告,不建议在命令行上命名的构建包。go list -m -u会打印所有弃用的依赖项(-f或者-json显示完整信息)。go命令基于不同的主版本区别不同模块,因此可以使用此机制,例如,提供具有一个新的主版本迁移的用户说明。

go get

go get -insecure标志已被删除。必需使用不安全的折衷方案获取依赖项时,需要设置GOINSECURE环境变量。-insecure标志也被用于绕过模块的sum校验,对于该用途,可以使用GOPRIVATE或者GONOSUMDB。

go get非主模块执行安装操作时打印弃用警告的命令(没有加-d选项)。应该使用go install cmd@version安装特定版本,可以使用如@latest或者@v1.2.3的后缀。 在Go 1.18 中,-d 标志将默认启用,并且go get仅用于更改依赖项go.mod。

go.mod文件缺少 go指令

如果主模块的 go.mod文件不包含一种go指令并且go命令无法更新go.mod文件,go命令为假定go版本为1.11而非当前版本。 ( 在go1.12中go mod init会自动添加go指令。)

如果模块依赖缺少显式的go.mod文件,或 其go.mod文件不包含go指令,go命令现在假定go版本为1.16而非当前版本。

有关vendor内容

如果主模块指定go 1.17或其后版本,go mod vendor现在注释 vendor/modules.txt go版本,每个供应商模块都有自己的go.mod文件,当从供应商的源代码构建模块的包时会注释go版本。

如果主模块指定go 1.17或其后版本,go mod vendor现在省略go.mod和go.sum供应商依赖项的文件,否则可能干扰其go命令来识别正确的模块根目录调用vendor树。

密码提示

当使用SSH获取Git存储库时,go命令现在默认禁止SSH密码提示和Git Credential Manager提示。用户访问带有受密码保护的SSH私有Git存储库可以配置ssh-agent,让go命令访问受密码保护的SSH密钥。

go mod download

go mod download调用时未指定参数,将不再将下载的模块的sum值保存到 go.sum。可能仍然对go.mod和go.sum需要加载构建列表。这与Go 1.15中默认行为一致。要保存所有模块的sum值,可使用

go mod download all

//go:build行

go命令现在可以理解//go:build行并且用来取代//+build行。新语法使用布尔表达式,更不容易出错。从1.17版本开始,新的语法被完全支持,所有的Go文件应该更新为具有相同含义的两种形式。为了帮助用户迁移,gofmt现在自动 同步两种形式。

go run

go run现在接受带有版本后缀的参数 (例如, go run example.com/cmd@v1.0.0)。 这使go run在模块感知模式下构建和运行包,忽略go.mod当前目录或任何父目录中的文件。对于运行可执行文件而无需安装它们或无需更改当前模块的依赖项。

gofmt

gofmt(和go fmt) 现在同步//go:build行与//+build行。如果一个文件只有//+build行,它们将被移动到适当的文件中的位置,并匹配添加//go:build行。 否则,//+build行将基于现有的//go:build行调整。

Vet

//go:build行和 //+build行不匹配的新警告

vet工具现在可以验证//go:build和//+build行位于文件的正确部分,并且彼此匹配。如果不匹配,gofmt可以用来修复它们。

signal.Notify在无缓冲通道上 的调用告警

Vet 工具现在对调signal.Notify输入信号被发送到一个无缓冲的通道时发出警告。signal.Notify不阻塞时使用无缓冲通道会有丢失发信号的风险。 例如:

c := make(chan os.Signal)//在channerl读取之前已经发出时候,该未缓冲的信号c会被丢失signal.Notify(c, os.Interrupt)c := make(chan os.Signal)

用户signal.Notify应该使用有足够缓冲空间的通道来跟上预期信号速率。

Is、As 和 Unwrap 方法的新警告

Vet工具现在会对命名的方法As, Is或者Unwrap在实现了erro类型的具有不同签名的接口和预期的errors不匹配时发出警告。errors.{As,Is,Unwrap}功能 实现同样的方法统一实现,而非Is(error) bool, As(interface{}) bool,或者Unwrap() error的各自实现。errors.{As,Is,Unwrap}将忽略具有相同的方法名称,但签名不同的方法。 例如:

type MyError struct { hint string }func (m MyError) Error() string { ... } // MyError实现错误.func (MyError) Is(target interface{}) bool { ... } // 目标是interface{}而不是错误。func Foo() bool {x, y := MyError{"A"}, MyError{"B"}return errors.Is(x, y) //返回false作为x != y并且 MyError 没有`Is(error) bool`函数}

Cover

cover工具现在使用优化的解析器golang.org/x/tools/cover,在解析大型覆盖率配置文件时可性能明显加快。

编译器

Go 1.17实现通过使用寄存器而非堆栈保持一种传递函数参数和结果的新方法。 基准测试显示,编译性能提升约5%,并且编译的二进制大小减少约2%。该功能目前已在Linux、macOS和Windows 64位架构启用(linux/amd64, darwin/amd64, 和 windows/amd64)。

该更改不会影响任何安全Go代码的功能并且旨在对大多数汇编代码没有影响。 但是可能会影响违反的代码这unsafe.Pointer访问函数参数时的规则,或者取决于涉及比较函数代码指针的未记录行为。

为了保持与现有汇编函数的兼容性,编译器生成适配器函数。在新的基于寄存器的调用约定和之前的基于堆栈的调用约定调用约定。这些适配器通常对用户不可见,除了汇编代码中Go函数的地址或取地址Go代码中的汇编函数使用reflect.ValueOf(fn).Pointer()或者unsafe.Pointer将返回地址适配器。依赖于这些代码指针值的代码可能无法按预期行事。适配器也可能在两个方面造成非常小的性能开销案例:通过Go 间接调用汇编函数一种fun值,并从程序集中调用Go函数。

运行时堆栈跟踪的格式(在未捕获的恐慌时打印)发生,或当runtime.Stack被称为)得到改进。之前,函数参数被打印为基于内存的十六进制字布局。现在源代码中的每个参数都单独打印,分开用逗号。聚合类型(结构、数组、字符串、切片、接口和复杂)参数由花括号分隔。一个警告是一个的值只存在于寄存器中而不存储到内存中的参数可能是不准确。 函数返回值(通常不准确)不再是打印。

现在可以内联包含闭包的函数。这种变化的一个影响是带有闭包的函数可能为每个地方产生一个不同的闭包代码指针函数是内联的。Go函数值不能直接比较,但这种变化可能会揭示使用的代码中的错误reflect要么unsafe.Pointer绕过此语言限制并通过代码指针比较函数。

链接器

当链接器使用外部链接模式时,默认的链接使用cgo程序时,调用链接器与-I选项,该选项现在将传递给外部链接器-Wl,--dynamic-linker选项。

核心库

Cgo

runtime/cgo包现提供了一种允许将任何Go值转换为安全表示的新工具可用于在C和Golang地传递值。

URL 查询解析

net/url和net/http之前接受的";"(分号)作为URL查询中的设置分隔符。新版本中,非百分比编码的设置分号将被拒绝并且net/htt服务器将会对包含分号的请求记录到Server.ErrorLog日志。

例如,在 Go 1.17 之前请求URL:cc?a=1;b=2&c=3会返回map[a:[1] b:[2] c:[3]], 现在返回为map[c:[3]]。

当遇到这样的查询字符串时,URL.Query和Request.FormValue忽略任何包含分号的设置,ParseQuery返回剩余的设置和错误,以及Request.ParseForm和 Request.ParseMultipartForm返回错误但仍基于其他配置设置Reques字段。

net/http用户可以通过使用AllowQuerySemicolons处理程序包装。它也不会触发ErrorLog日志记录警告。

注意,接受分号作为查询分隔符可能会导致安全问题。

TLS严格ALPN

当设置Config.NextProtos时,服务器现在强制协调配置协议和客户端建议的ALPN 协议。 如果有没有相互支持的协议,连接会被关闭。根据RFC 7301的要求会触发no_application_protocol告警,这可以缓解ALPACA 跨协议攻击 。

作为一个例外,当在服务器的 Config.NextProtos包含"h2"值时, HTTP/1.1客户端不支持的ALPN将被允许连接。

其他库改变

archive/zip

增加新方法File.OpenRaw, Writer.CreateRaw, Writer.Copy,关注运行性能时候可以使用。

compress/lzw

NewReader函数保证返回一个新的值类型Reader, NewWriter保证返回一个新的值类型Writer. 这些新类型都实现了Reset方法 ( Reader.Reset, Writer.Reset) 允许重用Reader或者Writer。

crypto/ed25519

crypto/ed25519包已被重写,所有amd64和arm64上的操作都提高两倍。

crypto/elliptic

CurveParams方法现在自动调用更快更安全的专用已知曲线(P-224、P-256 和 P-521)的实现。

注意,这是一种尽力而为的方法和应用程序应该避免使用泛型,而费常量时间 CurveParams方法,而是使用专用的Curv实现如P256.

P521曲线实现已使用由生成的代码重写fiat加密项目,它基于经过形式验证的算术模型操作。现在支持amd64 和arm 64。

crypto/rand

crypto/rand包现在macOS 上使用getentropy的系统调用,在Solaris、Illumos 和DragonFlyBSD系统中使用getrandom系统调用。

crypto/tls

新的Conn.HandshakeContext方法允许用户控制正在进行的的取消TLS握手。 提供的上下文可以通过新的回调从各种回调访问ClientHelloInfo.Context和 CertificateRequestInfo.Context方法。握手完成后取消上下文无效。

密码套件订购现在完全由crypto/tls打包。目前,密码套件是基于排序的考虑到其安全性、性能和硬件支持本地和对等方的硬件

Config.CipherSuites 字段现在被忽略,使用Config.PreferServerCipherSuites字段。注意 Config.CipherSuites仍然允许 应用程序选择要启用的TLS 1.0-1.2密码套件。

3DES密码套件已移至InsecureCipherSuites。它们仍然默认启用,但只是作为最后的备用方法。

从下一个版本 Go 1.18 开始,Config.MinVersion为crypto/tls客户端将默认使用TLS 1.2,禁用TLS 1.0和TLS 1.1。应用程序将能够覆盖更改明确设置 Config.MinVersion。但是不影响crypto/tls服务器。

crypto/x509

CreateCertificate如果提供的私钥不匹配父公钥,会返回错误。生成的证书将失败验证。

临时的 GODEBUG=x509ignoreCN=0标志已被移除。

ParseCertificate被重写,现在消耗的资源减少了约70%。除了错误消息外,行为没有改变。

在BSD系统上,/etc/ssl/certs现在搜索受信任根。这增加了对新系统可信证书存储的支持 FreeBSD 12.2+。

从下一个版本Go 1.18 开始,crypto/x509将拒绝使用SHA-1哈希函数签名的证书。但是不会限制自签名根证书。

database/sql

如果io.Close接口connector字段中的类型实现,DB.Close方法现在关闭。

新的NullInt16和NullByte结构表示可能为空的int16和字节值。 这些可以用作目的地Scan方法,类似于NullString。

debug/elf

添加了SHT_MIPS_ABIFLAGS常量。

encoding/binary

binary.Uvarint只读10字节以避免浪费的计算。如果超过10 bytes需要,返回的字节数是 -11.

在读取错误编码的varint 时,以前的Go版本可能会返回更大的负数。

encoding/csv

新的Reader.FieldPos方法返回对应于开始的行和列最近返回的记录中的给定字段Read.

encoding/xml

当评论出现在Directive,现在被替换使用单个空格而不是完全省略。

带有前导、尾随或多个的无效元素或属性名称冒号现在不加修改地存储到 Name.Local字段。

flag

如果指定了无效名称,标志声明现在会触发panic。

go/build

新的Context.ToolTags字段包含适合当前Go的构建标签工具链配置。

go/format

Sourc和Nod现在运行同步//go:build行与//+build行。 如果一个文件只有 // +build线,将移动到文件中的适当位置,并自动添加匹配的//go:build行。否则,//+build行将根据任何现有的//go:build行调整。

go/parser

新的SkipObjectResolution Modevalue 指示解析器解析时跳过这些指令。这可以提高解析速度。

iamge

图像类型(RGBA, Gray16等等)启用一个新的RGBA64Image接口。之前实现的具体类型draw.Image现在也实施draw.RGBA64Image,一种新image/draw打包。

io/fs

新的FileInfoToDirEntry函数转换一个FileInfo为DirEntry。

math

math包现在定义了另外三个常量:MaxUint,MaxInt和MinInt。对32位系统,其值范围为2^32-1, 2^31-1和-2^31。对64 位系统,其值范围为2^64 - 1, 2^63-1和-2^63。

mime

在Unix系统上,MIME 类型表现在从本地系统的共享MIME信息数据库。

mime/multipart

现在适用filepath.Base到返回值。减轻了潜在的路径遍历漏洞接受多部分消息的应用程序,例如net/http调用的服务器Request.FormFile。

net

新的IP.IsPrivate方法返回地址是否是为RFC 1918标准的私有IPv4地址或根据RFC 4193 标准的的本地 IPv6 地址。

Go DNS 解析器解析纯IPv4或纯IPv6网络的地址时仅发送一个DNS查询,而不是查询两个地址族。

ErrClosed sentine错误和ParseError错误类型现在这net.Error接口实现。

ParseIP和ParseCIDR函数现在拒绝包含零开始的十进制IPv4地址。这些组件在模块中被解释为十进制,但是在操作系统会将其视为八进制。如果使用Go应用程序验证IP地址,这种不匹配可能会导致安全问题然后以原始形式与非Go应用程序一起使用,这些应用程序将组件解释为八进制。一般来说,建议在验证后始终重新编码值,以避免该类问题。

net/http

在客户端或服务器中执行 TLS 握手时,net/http包现在使用新的(*tls.Conn).HandshakeContext和Request上下文。

设置Server ReadTimeout或者WriteTimeout字段为负值现在表示没有超时而不是立即超时。

ReadReques功能现在当请求有多个Host标头时返回错误。

在生成指向URL的重定向时,ServeMux现在总是使用相对URL Location标题。 此前会回显请求的完整URL,这可能会导致意外。

在解释由处理的某些HTTP标头时,非ASCII 字符现在被忽略或拒绝。

如果Request.ParseForm调用时返回错误Request.ParseMultipartForm, 后者现在继续填充Request.MultipartForm。

net/http/httptest

ResponseRecorder.WriteHeader现在当提供的代码不是有效的三位数HTTP状态代码时会触发panic。这符合的ResponseWriter实现的行为。

net/url

新方法Values.Has报告是否设置了查询参数。

os

File.WriteString方法已优化为不复制输入字符串。

reflect

新的Value.CanConver方法报告值是否可以转换为类型。这可用于在将切片转换为切片时避免恐慌 如果切片太短,则为数组指针类型。以前使用就足够了 Type.ConvertibleTo为此,但新方法允许的从切片到数组的转换即使类型是可转换的,指针类型也可能会触发pannic

新的StructField.IsExported和Method.IsExported方法报告是否导出结构字段或类型方法。它们提供了一种更具可读性的替代方法来检查PkgPath是否为空。

新的VisibleFields函数返回结构类型中的所有可见字段,包括匿名结构成员内的字段。

ArrayOf函数在以负长度调用时候,会触发panic。

检查Type.ConvertibleTo方法不再足以保证调用Value.Convert不会恐慌。 如果切片的长度小于N,则在将`[]T`转换为`*[N]T`时可能会触发panic。

runtime/metrics

添加了新指标来跟踪分配和释放的总字节数和对象。还增加了跟踪goroutine调度延迟分布的新指标。

runtime/pprof

块配置文件不再偏向于支持偶发的长事件频繁的短事件。

strconv

strconv包现在使用Ulf Adams的Ryū算法来格式化浮点数。该算法提高了大多数输入的性能,并且在最坏情况下的输入速度提高了99%以上。

新的QuotedPrefix功能 返回带引号的字符串(如理解 Unquote) 在输入的开始。

strings

Builder.WriteRune方法现在为负符文值写入替换字符U+FFFD。

sync/atomic

atomic.Value提供Swa和CompareAndSwap方法用于额外原子操作

syscall

GetQueuedCompletionStatus和PostQueuedCompletionStatus功能现已弃用。这些函数具有不正确的签名golang.org/x/sys/windows打包。

在类Unix系统上,子进程的进程组现在设置为阻塞信号。这避免了发送SIGTTOU当父进程在后台进程组中时发送给子进程。

Windows版本的SysProcAttr有两个新字段:AdditionalInheritedHandles是新要子继承的附加句柄列表过程。ParentProcess允许指定新进程的父进程。

常数MSG_CMSG_CLOEXEC现在定义在DragonFly和所有OpenBSD系统(它已经在 一些 OpenBSD 系统和所有FreeBSD、NetBSD 和Linux 系统)。

常数SYS_WAIT6和WEXITED 现在在NetBSD 系统上定义(SYS_WAIT6曾是已经在DragonFly和FreeBSD系统上定义; EXITED已经在 Darwin、DragonFly、 FreeBSD、Linux 和 Solaris 系统)。

test

添加了一个新的测试标志 -shuffle它控制测试和基准测试的执行顺序

新的T.Setenv和B.Setenv方法支持在持续时间内设置环境变量测试或基准。

text/template/parse

新的SkipFuncCheck Mode值将模板解析器更改为不验证函数是否已定义。

time

Time类型新提供GoString方法用于在打印时返回更有用的值%#v格式说明符在 fmt打包。

新的Time.IsDST方法可以用来检查时间是否在其配置的位置处于夏令时。

新的Time.UnixMilli和Time.UnixMicro方法返回UTC时间1970年1月1日以来经过的毫秒数和微秒数。

新的UnixMilli和UnixMicro功能返回本地Time对应于给定的Unix时间。

该包现在接受逗号“,”作为解析和格式化时间时小数秒的分隔符。例如,以下时间格式是合法的:

2006-01-02 15:04:05,999999999 -0700 MSTMon Jan _2 15:04:05,000000 2006Monday, January 2 15:04:05,000 2006

新常数Layout定义参考时间。

unicode

Is, IsGraphic, IsLetter, IsLower, IsMark, IsNumber, IsPrint, IsPunct, IsSpace, IsSymbol,和IsUpper函数对负符文值返false。