
Go 1.20 发布于 2023-02-01。它的特点是“很多小但实用”的变化：泛型约束更顺手，错误处理支持多错误，HTTP 和密码学 API 都补上了重要缺口。

源码侧 `api/go1.20.txt` 有 9165 条公开 API 增量，大量来自 FreeBSD RISC-V syscall；公开标准库目录新增 `arena`、`crypto/ecdh`、`runtime/coverage`。

## 主要变化

### 1. `comparable` 约束更符合直觉

Go 1.20 调整了泛型里 `comparable` 的规则，让更多实际可比较的类型能满足约束。这个变化让泛型 map key、set、去重函数等代码更容易表达。

```go
type Set[T comparable] map[T]struct{}
```

`comparable` 的实际意义是：类型值可以用于 `==`、`!=`，也可以作为 map key。对集合、索引、去重、缓存 key 这类泛型结构，它是最基础的约束。

### 2. 多错误成为标准表达

`errors.Join` 可以把多个错误组合成一个错误，`fmt.Errorf` 也支持多个 `%w`。`errors.Is` 和 `errors.As` 会沿着错误树查找。

```go
err := errors.Join(closeErr, flushErr)
if errors.Is(err, fs.ErrClosed) {
    ...
}
```

这个能力适合批量操作、并发任务和多阶段校验，因为它能保留多个失败原因，而不是只返回第一个。调用方仍然可以用标准错误匹配 API 判断其中是否包含某类错误。

### 3. `crypto/ecdh` 标准化密钥交换

ECDH 是现代 TLS、端到端加密和密钥协商里的基础组件。`crypto/ecdh` 提供统一 API，覆盖 NIST 曲线和 X25519 等常见曲线。

```go
curve := ecdh.X25519()
priv, _ := curve.GenerateKey(rand.Reader)
secret, _ := priv.ECDH(peerPublic)
```

标准库提供这层抽象后，应用不必直接拼装低层椭圆曲线细节。API 也更难误用：私钥、公钥和曲线关系都由类型表达。

### 4. 覆盖率可以脱离单次 `go test`

`runtime/coverage` 和 `go tool covdata` 让程序运行时写出覆盖率数据。集成测试、端到端测试、启动真实服务后的测试都能被纳入覆盖率分析。覆盖率因此不再只属于包级单元测试。

这对服务类程序很有用：可以用带覆盖率插桩的二进制跑一组 HTTP/API 测试，再把多个进程或多次运行的覆盖率数据合并。`go tool covdata` 负责合并、转换和输出。

## 语言与规范

Go 1.20 调整了 `comparable` 约束的规则，让更多可比较类型能满足泛型约束。它还支持从切片到数组或数组指针的转换，并补充了若干 unsafe 相关能力。

切片到数组/数组指针转换的价值在于把“长度检查 + 固定长度视图”写成语言能力：

```go
var b = []byte{1, 2, 3, 4}
p := (*[4]byte)(b)
a := [4]byte(b)
```

转换到数组指针不会复制底层数据，适合把一段切片按固定长度协议头解释。转换到数组会复制出一个数组值。两者都会检查长度，如果切片长度不足会 panic。这个能力能减少手写 `if len(b) < n` 后再 `copy` 的样板代码，但也要明确 panic 边界。

## 工具链与运行时

覆盖率工具链增强明显。`runtime/coverage` 和 `go tool covdata` 让覆盖率不再局限于 `go test` 的单进程测试，对集成测试、服务启动后采集覆盖率更有帮助。

PGO 进入预览阶段，编译器可以利用 profile 做优化。cgo、`go version`、`go test`、`go vet` 也有改进。

## 标准库与新增包

新增公开包：

- `crypto/ecdh`：标准 ECDH 密钥交换 API。
- `runtime/coverage`：运行时覆盖率数据写出。
- `arena`：实验性手动 arena 分配能力，需要实验开关。

重要变化：

- `errors.Join`、`fmt.Errorf` 多个 `%w`：表达多错误。
- `net/http.ResponseController`：统一控制 flush、hijack、deadline 等响应能力。
- `httputil.ReverseProxy.Rewrite`：比旧 `Director` 更安全清晰。

`ResponseController` 把原来分散在可选接口里的能力收拢到一个控制器上。handler 可以通过它设置读写 deadline、flush 响应、接管连接，而不用反复做类型断言判断 `ResponseWriter` 是否实现了某个扩展接口。

`arena` 的定位是把一批对象放进同一个生命周期里，最后一次性释放。它适合非常明确的短生命周期批处理，例如解析一批临时对象后整体丢弃。风险也很直接：arena 里的对象生命周期不再完全由普通 GC 模型表达，如果对象逃到 arena 外继续使用，就会出现难排查的问题。因此它是实验包，不适合作为普通业务代码的默认内存管理方式。

## 小版本特殊变化

Go 1.20 系列共有 14 个小版本。因为 1.20 加入多错误、ECDH 和覆盖率能力，小版本里能看到 `crypto/ecdh`、covdata、HTTP 和模板修复。

安全修复重点可以分成三组。第一组是 Web 输入：`net/http`、`mime/multipart`、`net/textproto`、`html/template`，对应请求解析、表单上传、头部解析和模板转义。第二组是路径和文件：`path/filepath`、`os`，对应路径清理、平台差异和文件边界。第三组是密码学：`crypto/tls`、`crypto/elliptic`、`crypto/x509`、`crypto/rsa`、`crypto/ecdh`，它们影响 TLS、证书、密钥交换和签名验证。`cmd/go` 安全修复说明构建工具本身也在威胁模型内。

- `go1.20.1` 修复 `crypto/tls`、`mime/multipart`、`net/http`、`path/filepath` 安全问题。
- `go1.20.2` 修复 `crypto/elliptic` 安全问题，同时修 covdata 命令、`crypto/ecdh`、`crypto/rsa`、`crypto/x509`。
- `go1.20.3` 修复 `go/parser`、`html/template`、`mime/multipart`、`net/http`、`net/textproto` 安全问题。
- `go1.20.4` 包含 3 个 `html/template` 安全修复。
- `go1.20.5` 包含 `cmd/go` 和 runtime 的 4 个安全修复，同时修 `crypto/rsa`、`net`、`os`。
- `go1.20.8` 包含 `html/template` 安全修复，并修 `crypto/tls`、`go/types`、`net/http`、`path/filepath`。
- `go1.20.10` 是 `net/http` 安全修复小版本。
- `go1.20.12` 修复 `go` 命令、`net/http`、`path/filepath` 安全问题。
- `go1.20.14` 修复 `crypto/x509`，是该系列最后一个小版本。

## 参考

- [Go 1.20 Release Notes](https://go.dev/doc/go1.20)
- [Go Release History](https://go.dev/doc/devel/release)
- [Go 1.20 source tag](https://go.googlesource.com/go/+/refs/tags/go1.20)
- [api/go1.20.txt](https://go.googlesource.com/go/+/refs/tags/go1.20/api/go1.20.txt)

