Go 1.15 版本说明
Go 1.15 简介
最新的 Go 版本 1.15 在 Go 1.14 发布六个月后发布。其大部分更改都体现在工具链、运行时和库的实现中。与往常一样,此版本维护了 Go 1 的 兼容性承诺。我们预计几乎所有 Go 程序都将像以前一样继续编译和运行。
Go 1.15 包括 对链接器的大量改进,改进了 在高核心数下分配小型对象,并弃用了 X.509 通用名称。GOPROXY
现在支持跳过返回错误的代理,并添加了一个新的 嵌入式 tzdata 软件包。
语言更改
语言没有更改。
端口
Darwin
如 Go 1.14 版本说明中 所述,Go 1.15 需要 macOS 10.12 Sierra 或更高版本;对先前版本的支持已停止。
如 Go 1.14 版本说明中 所述,Go 1.15 停止支持 macOS、iOS、iPadOS、watchOS 和 tvOS 上的 32 位二进制文件(darwin/386
和 darwin/arm
端口)。Go 继续支持 64 位 darwin/amd64
和 darwin/arm64
端口。
Windows
当提供 -buildmode=pie
cmd/link 标志时,Go 现在会生成 Windows ASLR 可执行文件。Go 命令在 Windows 上默认使用 -buildmode=pie
。
-race
和 -msan
标志现在始终启用 -d=checkptr
,它会检查 unsafe.Pointer
的用法。以前,除了 Windows 之外的所有操作系统都是这种情况。
Go 构建的 DLL 在收到信号(例如终端中的 Ctrl-C)时不再导致进程退出。
Android
在为 Android 链接二进制文件时,Go 1.15 会显式选择最近版本的 NDK 中提供的 lld
链接器。lld
链接器避免了某些设备上的崩溃,并计划在未来的 NDK 版本中成为默认的 NDK 链接器。
OpenBSD
Go 1.15 添加了对 GOARCH=arm
和 GOARCH=arm64
上的 OpenBSD 6.7 的支持。以前版本的 Go 已经在 GOARCH=386
和 GOARCH=amd64
上支持 OpenBSD 6.7。
RISC-V
在改进 Linux 上 64 位 RISC-V 端口(GOOS=linux
,GOARCH=riscv64
)的稳定性和性能方面取得了进展。它现在还支持异步抢占。
386
Go 1.15 是最后一个支持仅 x87 浮点硬件(GO386=387
)的版本。将来的版本将至少需要 SSE2 支持才能在 386 上运行,从而将 Go 的最低 GOARCH=386
要求提高到英特尔奔腾 4(2000 年发布)或 AMD Opteron/Athlon 64(2003 年发布)。
工具
Go 命令
GOPROXY
环境变量现在支持跳过返回错误的代理。代理 URL 现在可以使用逗号 (,
) 或管道字符 (|
) 分隔。如果代理 URL 后跟一个逗号,则 go
命令仅会在收到 404 或 410 HTTP 响应后尝试列表中的下一个代理。如果代理 URL 后跟一个管道字符,则 go
命令将在任何错误后尝试列表中的下一个代理。请注意,GOPROXY
的默认值仍然是 https://proxy.golang.org,direct
,在发生错误时不会回退到 direct
。
go
test
更改 -timeout
标志现在会使缓存的测试结果失效。当 go
test
再次使用较短的超时时间调用时,具有较长超时时间的测试运行的缓存结果将不再被视为通过。
标志解析
已修复 go
test
和 go
vet
中的各种标志解析问题。值得注意的是,GOFLAGS
中指定的标志处理更加一致,并且 -outputdir
标志现在将相对于 go
命令的工作目录(而不是每个单独测试的工作目录)解释相对路径。
模块缓存
现在可以使用 GOMODCACHE
环境变量设置模块缓存的位置。GOMODCACHE
的默认值为 GOPATH[0]/pkg/mod
,即此更改之前模块缓存的位置。
现在可以使用一种解决方法来解决 Windows 中访问模块缓存的 go
命令出现的“拒绝访问”错误,这些错误是由外部程序同时扫描文件系统引起的(请参阅 问题 #36568)。默认情况下不会启用此解决方法,因为当低于 1.14.2 和 1.13.10 的 Go 版本与同一模块缓存同时运行时,使用它是不安全的。可以通过显式设置环境变量 GODEBUG=modcacheunzipinplace=1
来启用它。
Vet
针对 string(x) 的新警告
vet 工具现在会警告 string(x)
形式的转换,其中 x
的整数类型不是 rune
或 byte
。Go 的使用经验表明,许多此类转换错误地假设 string(x)
会计算为整数 x
的字符串表示形式。它实际上会计算为包含 x
值的 UTF-8 编码的字符串。例如,string(9786)
不会计算为字符串 "9786"
;它会计算为字符串 "\xe2\x98\xba"
,或 "☺"
。
正确使用 string(x)
的代码可以重写为 string(rune(x))
。或者,在某些情况下,使用合适的字节切片 buf
调用 utf8.EncodeRune(buf, x)
可能是正确的解决方案。其他代码最有可能使用 strconv.Itoa
或 fmt.Sprint
。
使用 go
test
时,此新的 vet 检查默认启用。
我们正在考虑在将来的 Go 版本中禁止此转换。也就是说,语言将更改为仅在 x
的类型为 rune
或 byte
时允许对整数 x
使用 string(x)
。这样的语言更改将不向后兼容。我们正在使用此 vet 检查作为更改语言的第一步尝试。
针对不可能的接口转换的新警告
当类型断言始终失败时,vet 工具现在会警告从一个接口类型到另一个接口类型的类型断言。如果两种接口类型都实现了一个具有相同名称但类型签名不同的方法,则会发生这种情况。
没有理由编写始终失败的类型断言,因此应重写触发此 vet 检查的任何代码。
使用 go
test
时,此新的 vet 检查默认启用。
我们正在考虑在将来的 Go 版本中禁止不可能的接口类型断言。这样的语言更改将不向后兼容。我们正在使用此 vet 检查作为更改语言的第一步尝试。
运行时
如果 panic
使用类型派生自以下任何类型的值为参数调用:bool
、complex64
、complex128
、float32
、float64
、int
、int8
、int16
、int32
、int64
、string
、uint
、uint8
、uint16
、uint32
、uint64
、uintptr
,则会打印该值,而不仅仅是其地址。以前,这仅适用于这些类型的精确值。
在 Unix 系统上,如果 kill
命令或 kill
系统调用用于向 Go 程序发送 SIGSEGV
、SIGBUS
或 SIGFPE
信号,并且如果未通过 os/signal.Notify
处理该信号,则 Go 程序现在将可靠地崩溃并显示堆栈跟踪。在早期版本中,行为是不可预测的。
小型对象的分配现在在高核心数下性能要好得多,并且最坏情况下的延迟更低。
将一个小整数转换为接口值不再会导致分配。
已关闭通道上的非阻塞接收现在与开放通道上的非阻塞接收的性能一样好。
编译器
软件包 unsafe
的 安全规则 允许在调用某些函数时将 unsafe.Pointer
转换为 uintptr
。以前,在某些情况下,编译器允许进行多次链接转换(例如,syscall.Syscall(…,
uintptr(uintptr(ptr)),
…)
)。编译器现在要求恰好进行一次转换。应更新使用多次转换的代码以满足安全规则。
通过消除某些类型的 GC 元数据并更积极地消除未使用的类型元数据,Go 1.15 将典型的二进制文件大小减少了约 5%(与 Go 1.14 相比)。
工具链现在通过将函数对齐到 32 字节边界并填充跳转指令来缓解 GOARCH=amd64
上的 英特尔 CPU 勘误 SKX102。虽然此填充会增加二进制文件的大小,但这可以通过上面提到的二进制文件大小改进来弥补。
Go 1.15 向编译器和汇编器都添加了 -spectre
标志,以允许启用 Spectre 缓解措施。这些措施几乎永远不需要,主要作为一种“纵深防御”机制提供。有关详细信息,请参阅 Spectre Wiki 页面。
编译器现在会拒绝对应用于其声明没有意义的 //go:
编译器指令,并显示“错位的编译器指令”错误。此类误用指令以前已损坏,但编译器会静默忽略。
编译器的 -json
优化日志记录现在报告大型(>= 128 字节)复制并包含逃逸分析决策的说明。
链接器
此版本包括对 Go 链接器的重大改进,这些改进减少了链接器资源的使用(时间和内存)并提高了代码的健壮性和可维护性。
对于一组有代表性的大型 Go 程序,链接速度提高了 20%,内存使用量平均减少了 30%,适用于在 amd64
架构上运行的基于 ELF
的操作系统(Linux、FreeBSD、NetBSD、OpenBSD、Dragonfly 和 Solaris),其他架构/操作系统组合的改进幅度较小。
链接器性能提升的关键因素是重新设计的对象文件格式以及内部阶段的改进以提高并发性(例如,并行地将重定位应用于符号)。Go 1.15 中的对象文件略大于 1.14 中的等效文件。
这些更改是 使 Go 链接器现代化的多版本项目的一部分,这意味着将来版本中将会有更多链接器改进。
链接器现在在linux/amd64
和linux/arm64
上,对于-buildmode=pie
默认使用内部链接模式,因此这些配置不再需要C链接器。外部链接模式(在Go 1.14中,对于-buildmode=pie
是默认模式)仍然可以通过-ldflags=-linkmode=external
标志来请求。
Objdump
objdump
工具现在支持使用-gnu
标志以GNU汇编语法进行反汇编。
标准库
新的嵌入式tzdata包
Go 1.15包含一个新的包,time/tzdata
,它允许将时区数据库嵌入到程序中。导入此包(如import _ "time/tzdata"
)允许程序即使在本地系统上没有时区数据库的情况下也能找到时区信息。您也可以通过使用-tags timetzdata
构建来嵌入时区数据库。这两种方法都会使程序大小增加约800 KB。
Cgo
Go 1.15将把C类型EGLConfig
转换为Go类型uintptr
。此更改类似于Go 1.12及更高版本处理EGLDisplay
、Darwin的CoreFoundation和Java的JNI类型的方式。有关更多信息,请参阅cgo文档
。
在Go 1.15.3及更高版本中,cgo不允许Go代码在栈或堆上分配未定义的结构体类型(仅定义为struct S;
或类似的C结构体)。Go代码只能使用指向这些类型的指针。分配此类结构体的实例并将指针或完整的结构体值传递给C代码始终是不安全的,并且不太可能正常工作;现在禁止这样做。解决方法是:要么重写Go代码以仅使用指针,要么通过包含相应的C头文件来确保Go代码可以看到结构体的完整定义。
X.509 CommonName弃用
当没有主题备用名称时,将X.509证书上的CommonName
字段视为主机名的已弃用旧行为现在默认已禁用。可以通过将值x509ignoreCN=0
添加到GODEBUG
环境变量中来临时重新启用它。
请注意,如果CommonName
是一个无效的主机名,则无论GODEBUG
设置如何,它都会被忽略。无效名称包括包含字母、数字、连字符和下划线以外的任何字符的名称,以及包含空标签或尾随点的名称。
库的细微更改
与往常一样,对库进行了各种细微的更改和更新,并牢记Go 1的兼容性承诺
。
bufio
当Scanner
与一个无效的io.Reader
一起使用时,该Reader
从Read
中错误地返回了一个负数,Scanner
将不再出现panic,而是返回新的错误ErrBadReadCount
。
context
使用空父级创建派生Context
现在被明确禁止。使用WithValue
、WithDeadline
或WithCancel
函数尝试这样做会导致panic。
crypto
crypto/rsa
、crypto/ecdsa
和crypto/ed25519
包中的PrivateKey
和PublicKey
类型现在具有一个Equal
方法,用于比较密钥的等价性或为公钥创建类型安全的接口。该方法签名与go-cmp的等价性定义
兼容。
Hash
现在实现了fmt.Stringer
。
crypto/ecdsa
新的SignASN1
和VerifyASN1
函数允许以标准ASN.1 DER编码生成和验证ECDSA签名。
crypto/elliptic
新的MarshalCompressed
和UnmarshalCompressed
函数允许以压缩格式编码和解码NIST椭圆曲线点。
crypto/rsa
VerifyPKCS1v15
现在根据RFC 8017拒绝缺少前导零的无效短签名。
crypto/tls
新的Dialer
类型及其DialContext
方法允许使用上下文来连接并与TLS服务器进行握手。
Config
类型上的新VerifyConnection
回调允许为每个连接使用自定义验证逻辑。它可以访问ConnectionState
,其中包括对等证书、SCT和已钉扎的OCSP响应。
现在每24小时自动轮换一次自动生成的会话票证密钥,有效期为7天,以限制它们对前向保密的影响。
TLS 1.2及更早版本中会话票证的生存期(其中会话密钥被重复用于恢复的连接)现在限制为7天,同样是为了限制它们对前向保密的影响。
现在强制执行RFC 8446中指定的客户端降级保护检查。这可能会导致客户端遇到充当未经授权的降级攻击的中介时出现连接错误。
SignatureScheme
、CurveID
和ClientAuthType
现在实现了fmt.Stringer
。
ConnectionState
字段OCSPResponse
和SignedCertificateTimestamps
现在在客户端恢复的连接上重新填充。
tls.Conn
现在在永久断开的连接上返回一个不透明的错误,并包装临时net.Error
。要访问原始net.Error
,请使用errors.As
(或errors.Unwrap
)而不是类型断言。
crypto/x509
如果证书上的名称或正在验证的名称(使用VerifyOptions.DNSName
或VerifyHostname
)无效,则现在将不区分大小写地进行比较,而无需进一步处理(不遵守通配符或去除尾随点)。无效名称包括包含字母、数字、连字符和下划线以外的任何字符的名称,包含空标签的名称,以及证书上带有尾随点的名称。
新的CreateRevocationList
函数和RevocationList
类型允许创建符合RFC 5280的X.509 v2证书吊销列表。
CreateCertificate
现在如果模板是CA并且没有明确指定,则会自动生成SubjectKeyId
。
CreateCertificate
现在如果模板指定了MaxPathLen
但不是CA,则会返回错误。
在macOS以外的Unix系统上,SSL_CERT_DIR
环境变量现在可以是冒号分隔的列表。
在macOS上,二进制文件现在始终与Security.framework
链接以提取系统信任根,而不管cgo是否可用。结果行为应该与OS验证器更加一致。
crypto/x509/pkix
Name.String
现在如果ExtraNames
为空,则打印来自Names
的非标准属性。
database/sql
新的DB.SetConnMaxIdleTime
方法允许在连接空闲一段时间后将其从连接池中移除,而不管连接的总生命周期如何。DBStats.MaxIdleTimeClosed
字段显示由于DB.SetConnMaxIdleTime
而关闭的连接总数。
新的Row.Err
获取器允许在不调用Row.Scan
的情况下检查查询错误。
database/sql/driver
Conn
可以实现新的Validator
接口,以允许驱动程序发出信号指示连接是否有效或是否应该被丢弃。
debug/pe
该包现在定义了PE文件格式使用的IMAGE_FILE
、IMAGE_SUBSYSTEM
和IMAGE_DLLCHARACTERISTICS
常量。
encoding/asn1
Marshal
现在根据X.690 DER对SET OF的组件进行排序。
Unmarshal
现在拒绝根据X.690 DER未进行最小编码的标签和对象标识符。
encoding/json
该包现在对解码时的嵌套最大深度有一个内部限制。这减少了深度嵌套的输入可能使用大量栈内存,甚至导致“goroutine stack exceeds limit” panic的可能性。
flag
当flag
包看到-h
或-help
时,并且这些标志未定义,它现在会打印一条使用消息。如果FlagSet
是用ExitOnError
创建的,则FlagSet.Parse
将以状态2退出。在此版本中,-h
或-help
的退出状态已更改为0。特别是,这适用于命令行标志的默认处理。
fmt
打印动词%#g
和%#G
现在保留浮点值的尾随零。
go/format
Source
和Node
函数现在将数字文字前缀和指数规范化为Go源代码格式化的一部分。这与gofmt
命令的行为一致,该命令已自Go 1.13以来
实现。
html/template
该包现在在所有JavaScript和JSON上下文中使用Unicode转义符(\uNNNN
)。这修复了application/ld+json
和application/json
上下文中转义错误。
io/ioutil
TempDir
和TempFile
现在拒绝包含路径分隔符的模式。也就是说,诸如ioutil.TempFile("/tmp", "../base*")
之类的调用将不再成功。这可以防止意外的目录遍历。
math/big
新的Int.FillBytes
方法允许序列化到固定大小的预分配字节切片。
math/cmplx
此包中的函数已更新为符合C99标准(附录G IEC 60559兼容复数运算),关于处理特殊参数(如无穷大、NaN和带符号的零)。
net
如果I/O操作超过了Conn.SetDeadline
、Conn.SetReadDeadline
或Conn.SetWriteDeadline
方法设置的截止日期,它现在将返回一个错误,该错误是或包装了os.ErrDeadlineExceeded
。这可用于可靠地检测错误是否由于超过截止日期所致。早期版本建议在错误上调用Timeout
方法,但I/O操作可能会返回错误,对于这些错误,Timeout
返回true
,尽管截止日期尚未超过。
新的 Resolver.LookupIP
方法支持特定网络的 IP 查询,并接受上下文。
net/http
作为抵御请求走私攻击的加固措施,解析现在更加严格:非 ASCII 空格不再像 SP 和 HTAB 一样被修剪,并且放弃了对“identity
” Transfer-Encoding
的支持。
net/http/httputil
ReverseProxy
现在支持在传入的 Request.Header
映射条目中该字段为 nil
时不修改 X-Forwarded-For
标头。
当由 ReverseProxy
处理的切换协议(如 WebSocket)请求被取消时,后端连接现在会正确关闭。
net/http/pprof
所有配置文件端点现在都支持“seconds
”参数。如果存在,端点将在指定秒数内生成配置文件并报告差异。“seconds
”参数在 cpu
配置文件和跟踪端点中的含义保持不变。
net/url
新的 URL
字段 RawFragment
和方法 EscapedFragment
提供了有关特定片段的精确编码的详细信息并对其进行控制。它们类似于 RawPath
和 EscapedPath
。
新的 URL
方法 Redacted
以字符串形式返回 URL,其中任何密码都被替换为 xxxxx
。
os
如果 I/O 操作超过了由 File.SetDeadline
、File.SetReadDeadline
或 File.SetWriteDeadline
方法设置的截止时间,它现在将返回一个错误,该错误是或包含 os.ErrDeadlineExceeded
。这可用于可靠地检测错误是否由于超过截止时间导致。早期版本建议在错误上调用 Timeout
方法,但 I/O 操作可能会返回错误,对于这些错误,即使未超过截止时间,Timeout
也返回 true
。
os
和 net
包现在会自动重试因 EINTR
失败的系统调用。以前这会导致虚假错误,随着 Go 1.14 中添加了异步抢占,这种情况变得更加普遍。现在,这将被透明地处理。
os.File
类型现在支持 ReadFrom
方法。当使用 io.Copy
将数据从一个 os.File
复制到另一个 os.File
时,这允许在某些系统上使用 copy_file_range
系统调用。结果是,当复制到 os.File
时,io.CopyBuffer
并不总是使用提供的缓冲区。如果程序希望强制使用提供的缓冲区,可以通过编写 io.CopyBuffer(struct{ io.Writer }{dst}, src, buf)
来实现。
plugin
现在在 macOS 上支持(并默认启用)-buildmode=plugin
的 DWARF 生成。
现在在 freebsd/amd64
上支持使用 -buildmode=plugin
构建。
reflect
reflect
包现在不允许访问所有非导出字段的方法,而之前它允许访问非导出嵌入字段的方法。依赖于先前行为的代码应更新为改为访问封闭变量的相应提升方法。
regexp
新的 Regexp.SubexpIndex
方法返回正则表达式中具有给定名称的第一个子表达式的索引。
runtime
包括 ReadMemStats
和 GoroutineProfile
在内的几个函数,如果垃圾回收正在进行,则不再阻塞。
runtime/pprof
goroutine 配置文件现在包括与在分析时每个 goroutine 关联的配置文件标签。此功能尚未在 debug=2
报告的配置文件中实现。
strconv
添加了 FormatComplex
和 ParseComplex
用于处理复数。
FormatComplex
将复数转换为 (a+bi) 形式的字符串,其中 a 和 b 分别是实部和虚部。
ParseComplex
将字符串转换为指定精度的复数。ParseComplex
接受 N+Ni
格式的复数。
sync
新方法 Map.LoadAndDelete
原子地删除键并返回先前存在的值(如果存在)。
Map.Delete
方法效率更高。
syscall
在 Unix 系统上,使用 SysProcAttr
的函数现在将拒绝尝试同时设置 Setctty
和 Foreground
字段,因为它们都使用 Ctty
字段,但使用方法不兼容。我们预计很少有现有程序同时设置这两个字段。
设置 Setctty
字段现在要求将 Ctty
字段设置为子进程中的文件描述符编号,该编号由 ProcAttr.Files
字段确定。使用子描述符始终有效,但在某些情况下,使用父文件描述符也碰巧有效。一些设置 Setctty
的程序需要更改 Ctty
的值以使用子描述符编号。
现在可以在 windows/amd64
上调用返回浮点值的系统调用。
testing
testing.T
类型现在具有 Deadline
方法,该方法报告测试二进制文件超过超时的时间。
TestMain
函数不再需要调用 os.Exit
。如果 TestMain
函数返回,测试二进制文件将使用 m.Run
返回的值调用 os.Exit
。
新方法 T.TempDir
和 B.TempDir
返回在测试结束时自动清理的临时目录。
go
test
-v
现在按测试名称对输出进行分组,而不是在每一行上打印测试名称。
text/template
JSEscape
现在始终使用 Unicode 转义序列 (\u00XX
),这与 JSON 兼容。
time
新方法 Ticker.Reset
支持更改滴答器的持续时间。
在返回错误时,ParseDuration
现在引用原始值。