支持静态资源嵌入
参见之前的文章:Go1.16新特性|embed静态资源嵌入
新增 io/fs 的支持
Go 1.16 标准库新增 io/fs 包,并定义了一个 fs.File 接口用于表示一个只读文件树 (tree of file) 的抽象。
io/fs 包的两个最重要的接口如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// $GOROOT/src/io/fs/fs.go
// An FS provides access to a hierarchical file system.
//
// The FS interface is the minimum implementation required of the file system.
// A file system may implement additional interfaces,
// such as ReadFileFS, to provide additional or optimized functionality.
type FS interface {
// Open opens the named file.
//
// When Open returns an error, it should be of type *PathError
// with the Op field set to "open", the Path field set to name,
// and the Err field describing the problem.
//
// Open should reject attempts to open names that do not satisfy
// ValidPath(name), returning a *PathError with Err set to
// ErrInvalid or ErrNotExist.
Open(name string) (File, error)
}
// A File provides access to a single file.
// The File interface is the minimum implementation required of the file.
// A file may implement additional interfaces, such as
// ReadDirFile, ReaderAt, or Seeker, to provide additional or optimized functionality.
type File interface {
Stat() (FileInfo, error)
Read([]byte) (int, error)
Close() error
}
|
FS 接口代表虚拟文件系统的最小抽象,File 接口则是虚拟文件的最小抽象,我们可以基于这两个接口进行扩展以及对接现有的一些实现。io/fs 包也给出了一些扩展 FS 的 “样例”:
废弃 io/ioutil
Go 官方认为 io/ioutil 这个包的定义不明确且难以理解。所以 Russ Cox 在 2020.10.17 提出了废弃 io/ioutil 的提案。
大致变更如下:
1
2
3
4
5
6
7
8
|
Discard => io.Discard
NopCloser => io.NopCloser
ReadAll => io.ReadAll
ReadDir => os.ReadDir
ReadFile => os.ReadFile
TempDir => os.MkdirTemp
TempFile => os.CreateTemp
WriteFile => os.WriteFile
|
与此同时大家也不需要担心存在破坏性变更,因为有 Go1 兼容性的保证,在 Go1 中 io/ioutil 还会存在,只变更内部的方法调用:
1
2
3
4
5
6
7
|
func ReadAll(r io.Reader) ([]byte, error) {
return io.ReadAll(r)
}
func ReadFile(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
|
大家在后续也可以改改调用习惯。
net 包的变化
在 Go 1.16 之前,我们检测在一个已关闭的网络上进行 I/O 操作或在 I/O 完成前网络被关闭的情况,只能通过匹配字符串”use of closed network connection” 的方式来进行。之前的版本没有针对这个错误定义 “哨兵错误变量”(更多关于哨兵错误变量的内容,可以参考我的专栏文章《别笑!这就是 Go 的错误处理哲学》),Go 1.16 增加了 ErrClosed 这个 “哨兵错误变量”,我们可以通过 errors.Is (err, net.ErrClosed)
来检测是否是上述错误情况。
调整切片扩容策略
Go1.16 以前的 slice 的扩容条件是 len,在最新的代码中,已经改为了以 cap 属性作为基准:
1
2
3
4
5
6
7
8
9
|
// src/runtime/slice.go
if cap > doublecap {
newcap = cap
} else {
// 这是以前的代码:if old.len < 1024 {
// 下面是 Go1.16rc1 的代码
if old.cap < 1024 {
newcap = doublecap
}
|
以官方的 test case 为例:
1
2
3
4
5
6
7
|
func main() {
const N = 1024
var a [N]int
x := cap(append(a[:N-1:N], 9, 9))
y := cap(append(a[:N:N], 9))
println(cap(x), cap(y))
}
|
在 Go1.16 以前输出 2048, 1280。在 Go1.16 及以后输出 1280, 1280,保证了两种的一致。
简化结构体标签
在 Go 语言的结构体中,我们常常会因为各种库的诉求,需要对结构体的 tag 设置标识。
如果像是以前,量比较多就会变成:
1
2
3
|
type MyStruct struct {
Field1 string `json:"field_1,omitempty" bson:"field_1,omitempty" xml:"field_1,omitempty" form:"field_1,omitempty" other:"value"`
}
|
但在 Go1.16 及以后,就可以通过合并的方式:
1
2
3
|
type MyStruct struct {
Field1 string `json,bson,xml,form:"field_1,omitempty" other:"value"`
}
|
方便和简洁了不少。
参考
Go 1.16 Release Notes
Go 1.16 中值得关注的几个变化
Go1.16 即将正式发布,以下变更你需要知道