支持静态资源嵌入

参见之前的文章: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 即将正式发布,以下变更你需要知道