Go 1.17的介绍
Go 1.17尚未发布。这些是进行中的发布说明。Go 1.17预计将在2021年8月发布。
变化
Go 1.17包括对语言的三个小的增强。
- 从片断到数组指针的转换。类型为
[]T
的表达式s
现在可以转换为数组指针类型*[N]T
。如果a
是这种转换的结果,那么在范围内的相应指数指的是相同的底层元素:&a[i] == &s[i]
对于0 <= i < N
。如果len(s)
小于N
,则转换会陷入困境。 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
时仍然必须遵循这些规则。
端口
Darwin
正如 Go 1.16 发行说明中所宣布的,Go 1.17 需要 macOS 10.13 High Sierra 或更高版本;对以前版本的支持已经停止。
Windows
Go 1.17 增加了对 Windows 上 64 位 ARM 架构的支持(windows/arm64
端口)。该端口支持 cgo。
OpenBSD
OpenBSD 上的 64 位 MIPS 架构 (openbsd/mips64
port) 现在支持 cgo。
在 Go 1.16 中, 在 OpenBSD 的 64 位 x86 和 64 位 ARM 架构上 (openbsd/amd64
和openbsd/arm64
port) 系统调用是通过libc
进行的, 而不是直接使用机器指令。在 Go 1.17 中,OpenBSD 上的 32 位 x86 和 32 位 ARM 架构(openbsd/386
和openbsd/arm
端口)也是如此。这确保了与未来版本的 OpenBSD 的前瞻性兼容,特别是与 OpenBSD 6.9 之后的版本,后者要求非静态 Go 二进制文件的系统调用通过libc
进行。
ARM64
Go 程序现在可以在所有操作系统的 64 位 ARM 架构上维护堆栈框架指针。此前,它只在 Linux、macOS 和 iOS 上维护堆栈框架指针。
Go 命令
懒惰的模块加载
如果一个模块在其go.mod
文件中指定了go
1.17
或更高,那么它的横向需求现在会被lazy加载,避免了为其他不相关的依赖下载或读取go.mod
文件。为了支持懒惰加载,在Go 1.17模块中,go
命令在go.mod
文件中为每个依赖关系提供了被模块中的任何包或测试过渡导入的明确需求。
因为go.mod文件中额外的显式需求的数量可能很大,在Go 1.17模块中,新增加的间接依赖的需求被维护在一个单独的require
,而不是包含直接依赖的块中。
为了方便升级到懒惰加载,go
mod
tidy
子命令现在支持一个-go
标志来设置或改变go.mod
文件中的go
版本。要启用现有模块的懒惰加载而不改变其依赖的选定版本,请运行。
go mod tidy -go=1.17
默认情况下,go
mod
tidy
会验证与主模块相关的依赖项的选定版本是否与之前的 Go 版本(Go 1.16 指明了go
1.17
的模块)所使用的版本相同,并保留该版本所需的go.sum
项,即使是其他命令通常不需要的依赖项。
-compat
标志允许该版本被覆盖,以支持较早(或仅较新)的版本,直到go.mod
文件中的go
指令指定的版本。整理一个go
1.17
模块,只用于Go 1.17,不保存Go 1.16的校验和(或检查与Go 1.16的一致性)。
go mod tidy -compat=1.17
请注意,即使主模块用-compat=1.17
,从go
1.16
或更早的模块中require
的用户仍然可以使用该模块,只要软件包只使用兼容的语言和库特性。
模块作者可以通过在go.mod
上添加一个// Deprecated:
的注释来废除一个模块,然后标记一个新的版本。go
get
现在,如果构建命令行上命名的软件包所需的模块被废除,将打印出一个警告。go
list
-m
-u
打印所有依赖关系的废除信息(使用-f
或-json
来显示完整信息)。go
命令认为不同的主版本是不同的模块,所以这个机制可以用来为用户提供新主版本的迁移说明。
go
get
go
get
-insecure
标志已被废弃,并已被删除。为了在获取依赖关系时允许使用不安全的方案,请使用GOINSECURE
环境变量。-insecure
标志也绕过了模块总和验证,如果你需要该功能,请使用GOPRIVATE
或GONOSUMDB
。详见go
help
environment
。
go.mod
文件缺少go
指令
如果主模块的go.mod
文件不包含go
指令,并且go
命令不能更新go.mod
文件,go
命令现在假定go 1.11
而不是当前版本。(go
mod
init
从Go 1.12开始自动添加了go
指令。)
如果一个模块的依赖关系缺乏明确的go.mod
文件,或者其go.mod
文件不包含go
指令,go
命令现在假定该依赖关系为go 1.16
,而不是当前的版本。(在 GOPATH 模式下开发的依赖关系可能缺乏go.mod
文件,而vendor/modules.txt
迄今为止从未记录过依赖关系的go.mod
文件所显示的go
版本。)
vendor
内容
如果主模块指定go
1.17
或更高,go
mod
vendor
现在用每个销售模块在其自己的go.mod
文件中指示的go
版本来注释vendor/modules.txt
。注释的版本在从供应商的源代码构建模块的包时被使用。
如果主模块指定了go
1.17
或更高的版本,go
mod
vendor
现在省略了go.mod
和go.sum
文件的厂商依赖关系,否则当在vendor
树中调用时,会干扰go
命令识别正确模块根的能力。
密码提示
go
命令在使用 SSH 获取 Git 仓库时,现在默认会抑制 SSH 密码提示和 Git 凭证管理器的提示,就像之前对其他 Git 密码提示所做的那样。使用受密码保护的SSH认证私人Git仓库的用户可以配置一个ssh-agent
,使go
命令使用受密码保护的SSH密钥。
go
mod
download
当go
mod
download
在没有参数的情况下被调用时,它将不再把下载的模块内容的和保存到go.sum
。它仍然可能对go.mod
和go.sum
进行修改,以加载构建列表。这与Go 1.15中的行为相同。要保存所有模块的总和,请使用go
mod
download
all
。
//go:build
行
go
命令现在可以理解//go:build
行,并且比// +build
行更喜欢它们。新的语法使用布尔表达式,就像Go一样,应该不容易出错。 从这个版本开始,新的语法被完全支持,所有的Go文件应该被更新为具有相同含义的两种形式。为了帮助迁移。 gofmt
现在会自动同步这两种形式。关于语法和迁移计划的更多细节,见 golang.org/design/draf…
gofmt
gofmt
(和go
fmt
)现在将//go:build
行与// +build
行同步。如果一个文件只有// +build
行,它们将被移到文件中的适当位置,而匹配的//go:build
行将被添加。否则,// +build
行将根据任何现有的//go:build
行被覆盖。欲了解更多信息,请参阅golang.org/design/draf…
Vet
新增对不匹配的//go:build
和// +build
行的警告。
vet
工具现在会验证//go:build
和// +build
行是否在文件的正确部分并相互同步。如果它们不在一起。 gofmt
可以用来修复它们。欲了解更多信息,请参见golang.org/design/draf…
对在未缓冲的通道上调用signal.Notify
的新警告
vet工具现在对调用signal.Notify与传入信号被发送到一个未缓冲的通道发出警告。使用未缓冲的通道有可能错过在其上发送的信号,因为signal.Notify
,在向通道发送时不会被阻止。比如说。
c := make(chan os.Signal)
// signals are sent on c before the channel is read from.
// This signal may be dropped as c is unbuffered.
signal.Notify(c, os.Interrupt)
signal.Notify
的用户应该使用有足够缓冲空间的通道,以跟上预期的信号速率。
Is, As 和 Unwrap 方法的新警告
vet工具现在会对实现error
接口的类型上名为As
,Is
或Unwrap
的方法发出警告,这些方法的签名与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 implements error.
func (MyError) Is(target interface{}) bool { ... } // target is interface{} instead of error.
func Foo() bool {
x, y := MyError{"A"}, MyError{"B"}
return errors.Is(x, y) // returns false as x != y and MyError does not have an `Is(error) bool` function.
}
cover
cover
工具现在使用来自golang.org/x/tools/cover
的优化解析器,这在解析大型覆盖率剖面时可能会明显加快。
编译器
Go 1.17 实现了一种使用寄存器而不是堆栈来传递函数参数和结果的新方法。这项工作在 64 位 x86 架构的 Linux、MacOS 和 Windows 上启用(linux/amd64
,darwin/amd64
,windows/amd64
端口)。对于一组有代表性的Go包和程序,基准测试显示性能提高了约5%,二进制大小减少了约2%。
这一变化并不影响任何安全Go代码的功能。它可以影响到兼容性准则之外的代码,影响很小。为了保持与现有汇编函数的兼容性,有时会使用在新的基于寄存器的调用惯例和以前基于堆栈的调用惯例之间转换的适配器函数(也称为ABI包装器)。这对用户来说大多是不可见的,除了那些在Go中获取地址的汇编函数。使用reflect.ValueOf(fn).Pointer()
(或类似的方法,如通过unsafe.Pointer
)来获取汇编函数的地址,现在将返回ABI包装器的地址。除了特殊用途的汇编代码(如访问线程本地存储或需要特殊的堆栈对齐),这大多是无害的。从 Go 中通过func
值间接调用的汇编函数现在将通过 ABI 包装器进行,这可能会导致非常小的性能开销。另外,从汇编中调用 Go 函数现在可以通过 ABI 封装器,这可能会造成非常小的性能开销。
来自运行时的堆栈跟踪的格式(在发生未捕获的panic时或调用runtime.Stack
时打印)得到了改进。以前,函数参数被打印成基于内存布局的十六进制。现在,源代码中的每个参数都被单独打印,用逗号隔开。聚合类型(结构体、数组、字符串、片断、接口和复合体)的参数用大括号分隔。需要注意的是,只存在于寄存器中而没有存储到内存中的参数的值可能是不准确的。结果(通常是不准确的)不再被打印。
包含闭包的函数现在可以被内联了。这一变化的一个影响是,一个带有闭包的函数实际上可能为该函数被内联的每个地方产生一个不同的闭包函数。因此,这一变化可能揭示了Go函数通过指针值进行比较(不正确)的错误。根据定义,Go 函数是不可比较的。
核心库
Cgo
runtime/cgo包现在提供了一个新的工具,可以将任何 Go 值转化为安全的表示,可以用来在 C 和 Go 之间安全地传递值。更多信息请参见runtime/cgo.Handle。
小改动
像往常一样,在考虑到Go 1的兼容性承诺的前提下,对库进行了各种小的修改和更新。
新方法 File.OpenRaw
, Writer.CreateRaw
, Writer.Copy
为那些以性能为首要考虑的情况提供支持。
该 Writer.WriteRune
方法现在为负符文值写入替换字符U+FFFD,正如它对其他无效符文所做的那样。
Buffer.WriteRune
方法现在为负的符文值写入替换字符U+FFFD,正如它对其他无效符文所做的那样。
该 NewReader
函数被保证返回一个新类型的值 Reader
的值,同样地 NewWriter
保证返回一个新类型的值。 Writer
这些新类型都实现了一个Reset
方法(Reader.Reset
, Writer.Reset
),允许重复使用Reader
或Writer
。
crypto/ed25519
包已被重写,现在所有操作在amd64和arm64上的速度大约是两倍。其他可观察的行为没有改变。
CurveParams
方法现在自动调用更快、更安全的已知曲线(P-224、P-256和P-521)的专用实现。请注意,这是一个尽力而为的方法,应用程序应避免使用通用的、非恒定时间的CurveParams
方法,而是使用专用的 Curve
实现,例如 P256
.
P521
曲线的实现已经用Fiat-crypto项目产生的代码重写了,它是基于一个正式验证的算术运算模型。它现在是恒定时间的,在amd64和arm64上快三倍。可观察到的行为并没有其他变化。
crypto/rand
包现在在 macOS 上使用getentropy
系统调用,在 Solaris、Illumos 和 DragonFlyBSD 上使用getrandom
系统调用。
这个新的 Conn.HandshakeContext
方法允许用户控制取消一个正在进行的TLS握手。提供的上下文可以从各种回调中通过新的 ClientHelloInfo.Context
和 CertificateRequestInfo.Context
方法从各种回调中访问所提供的上下文。在握手结束后取消上下文是没有效果的。
当 Config.NextProtos
被设置时,服务器现在会强制要求配置的协议和客户端公布的协议之间存在重叠(如果有的话)。 如果没有重叠,则会按照 RFC 7301 的要求,用no_application_protocol
的警告关闭连接。
密码套件排序现在完全由crypto/tls
包处理。目前,密码套件的排序是基于其安全性、性能和硬件支持,同时考虑到本地和对等的硬件。的顺序。 Config.CipherSuites
字段的顺序现在被忽略,同时也忽略了 Config.PreferServerCipherSuites
字段。请注意,Config.CipherSuites
仍然允许应用程序选择启用哪些 TLS 1.0-1.2 密码套件。
3DES密码套件已被移至 InsecureCipherSuites
由于基本的块大小相关的弱点。它们仍然是默认启用的,但只是作为最后的手段,这要归功于上面的密码套件排序变化。
CreateCertificate
如果提供的私钥与父方的公钥不匹配(如果有的话),现在会返回一个错误。由此产生的证书将无法验证。
临时的GODEBUG=x509ignoreCN=0
标志已被删除。
ParseCertificate
已被重写,现在消耗的资源减少了~70%。除了错误信息外,可观察到的行为并没有其他变化。
在 BSD 系统上,/etc/ssl/certs
现在会被搜索到受信任的根。这增加了对 FreeBSD 12.2+ 中新的系统可信证书存储的支持。
DB.Close
方法现在关闭了connector
字段,如果该字段中的类型实现了 io.Closer
接口。
新的 NullInt16
和 NullByte
结构表示可能为空的int16和字节值。这些可以作为 Scan
方法的目的地,类似于NullString。
SHT_MIPS_ABIFLAGS
常量已被添加。
binary.Uvarint
将在10 bytes
之后停止读取,以避免计算的浪费。如果需要超过10 bytes
,返回的字节数是-11
。
以前的Go版本在读取不正确的编码varints时可能会返回更大的负数。
新的 Reader.FieldPos
方法返回最近的记录中给定字段的开始位置所对应的行和列,该记录由 Read
.
标志声明现在会在指定无效的名称时出现恐慌。
新的 Context.ToolTags
字段持有适合当前 Go 工具链配置的构建标签。
该 Source
和 Node
函数现在将//go:build
行与// +build
行同步。如果文件中只有// +build
行,它们将被移到文件中的适当位置,并添加匹配的//go:build
行。否则,// +build
行将根据任何现有的//go:build
行被覆盖。欲了解更多信息,请参阅golang.org/design/draf…
这个新 FileInfoToDirEntry
函数将一个FileInfo
转换为DirEntry
。
数学包现在又定义了三个常量:MaxUint
,MaxInt
和MinInt
。对于32位系统,它们的值分别为2^32 - 1
,2^31 - 1
和-2^31
。对于64位系统,它们的值分别为2^64 - 1
,2^63 - 1
和-2^63
。
在 Unix 系统中,MIME 类型表现在会从本地系统的共享 MIME-info 数据库中读取。
这个新方法 IP.IsPrivate
报告一个地址是否为RFC 1918规定的私有IPv4地址或RFC 4193规定的本地IPv6地址。
Go DNS 解析器在解析纯 IPv4 或纯 IPv6 网络的地址时,现在只发送一次 DNS 查询,而不是同时查询两个地址族。
ErrClosed
哨兵错误和 ParseError
错误类型现在实现了 net.Error
接口。
ParseIP
和 ParseCIDR
函数现在拒绝包含有前导零的十进制组件的IPv4地址。 这些组件总是被解释为十进制,但一些操作系统将其视为八进制。 如果Go应用程序用于验证IP地址,然后将其以原始形式用于将组件解释为八进制的非Go应用程序,这种不匹配可能导致安全问题。一般来说,建议在验证后总是重新编码,这样可以避免这类解析器错位的问题。
该 net/http
包现在使用新的 (*tls.Conn).HandshakeContext
与 Request
上下文,当在客户端或服务器中执行TLS握手时。
将 Server
ReadTimeout
或WriteTimeout
字段为负值,现在表示没有超时而不是立即超时。
ReadRequest
函数现在会在请求有多个Host头时返回一个错误。
ResponseRecorder.WriteHeader>
现在,当提供的代码不是有效的三位数HTTP状态代码时,会出现恐慌。 这符合 ResponseWriter>
中的实现的行为一致。 net/http
包中的实现的行为。
这个新方法 Values.Has
报告一个查询参数是否被设置。
该 File.WriteString
方法进行了优化,不再对输入字符串进行复制。
新的 StructField.IsExported
和 Method.IsExported
方法报告是否导出了一个结构字段或类型方法。它们提供了一个更易读的替代方法来检查PkgPath
是否为空。
新的 VisibleFields
函数返回一个结构类型中的所有可见字段,包括匿名结构成员中的字段。
该 ArrayOf
函数在调用长度为负数时,现在会出现恐慌。
增加了新的指标,跟踪分配和释放的总字节数和对象。还增加了一个跟踪goroutine调度延迟分布的新指标。
区块配置文件不再偏向于支持不经常发生的长事件而不是经常发生的短事件。
strconv
包现在使用 Ulf Adams 的 Ryū 算法来格式化浮点数。这种算法在大多数输入上都提高了性能,而且在最坏情况下的输入上要快 99% 以上。
新的 QuotedPrefix
函数返回输入开始时的带引号的字符串(由 Unquote
的理解)。
该 Builder.WriteRune
方法现在为负符文值写入替换字符U+FFFD,就像它对其他无效符文一样。
atomic.Value
现在有 Swap
和 CompareAndSwap
方法,提供额外的原子操作。
该 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 系统上被定义;WEXITED
已经在 Darwin、 DragonFly、 FreeBSD、 Linux 和 Solaris 系统上被定义)。
添加了一个新的测试标志 -shuffle
,用于控制测试和基准的执行顺序。
新的 T.Setenv
和 B.Setenv
方法支持为测试或基准的持续时间设置一个环境变量。
新的 SkipFuncCheck
Mode
值改变了模板解析器,使其不验证函数是否被定义。
Time
类型现在有一个 GoString
方法,当使用fmt
包中的%#v
格式指定符打印时,将返回一个更有用的时间值。
这个新的 Time.IsDST
方法可用于检查时间在其配置的位置是否处于夏令时。
新的 Time.UnixMilli
和 Time.UnixMicro
方法分别返回自1970年1月1日UTC以来经过的毫秒和微秒数。
新的UnixMilli
和UnixMicro
函数返回与给定Unix时间相对应的本地时间。
在解析和格式化时间时,该软件包现在接受逗号", "作为小数秒的分隔符。 现在接受以下时间格式。
- 2006-01-02 14:06:03,99999999 -0700 MST
- Mon Jan _2 14:06:03,120007 2006 年
- Mon Jan 2 14:06:03,120007 2006年
新的常数 Layout
定义了参考时间。
The Is
, IsGraphic
, IsLetter
, IsLower
, IsMark
, IsNumber
, IsPrint
, IsPunct
, IsSpace
, IsSymbol
, 和 IsUpper
函数现在对负的符文值返回false
,就像它们对其他无效的符文一样。