本系列旨在梳理 Go 的 release notes 与发展史,来更加深入地理解 Go 语言设计的思路。

Go 1.11 值得关注的改动:
- WebAssembly 支持 : Go 1.11 实验性地增加了对 WebAssembly (js/wasm) 的支持,允许将 Go 程序编译成可在浏览器中运行的
.wasm
文件。编译产物包含 Go 运行时,因此体积较大(约 2MB,压缩后 500KB),并提供了实验性的syscall/js
包与 JavaScript 进行交互。同时,新增了GOOS="js"
和GOARCH="wasm"
,命名符合*_js.go
或*_wasm.go
规则的文件现在仅在对应编译目标下生效。 - Go 模块 (Go Modules) : Go 1.11 初步引入了 Go 模块 (Go Modules) 作为
GOPATH
的替代方案,提供了内置的版本控制和包分发支持。虽然仍处于实验阶段,但其目标是让开发者不再局限于GOPATH
工作区,并改善依赖管理和构建的可复现性。 - 编译器优化 : 编译器现在会报告在类型断言
switch
语句的guard
中声明但未被使用的变量为错误,例如switch x := v.(type) {}
中的x
若未使用,编译将失败。这增强了代码的严谨性,与gccgo
和go/types
的行为保持了一致。
下面是一些值得展开的讨论:
Go 模块 (Go Modules) 详解
Go 1.11 版本引入了对 Go 模块 (Go Modules) 的初步支持,这是 Go 语言在包管理和版本依赖方面的一个重大变革,旨在解决长期以来 GOPATH
模式带来的诸多问题。Modules 提供了一种新的方式来管理项目依赖,集成了版本控制和包分发功能,使得开发者可以:
- 在
GOPATH
之外创建和管理项目。 - 通过
go.mod
文件明确、轻量地记录项目的版本依赖信息。 - 实现更可靠、更可复现的构建过程。
尽管在 Go 1.11 中 Modules 仍处于实验阶段,其细节可能会在后续版本中调整,但官方保证使用 Go 1.11 迁移到 Modules 的项目将在 Go 1.12 及以后版本中继续工作。
Go 1.10 (及更早) 的 GOPATH
模式
在 Go 1.11 之前,Go 项目的开发和依赖管理主要依赖 GOPATH
环境变量。
- 工作区限制 :所有的 Go 源代码(包括你自己的项目和第三方依赖)都必须放在
$GOPATH/src
目录下,形成了固定的目录结构,例如$GOPATH/src/github.com/user/project
。 - 依赖获取 :使用
go get
命令下载依赖包,默认会下载最新的代码到$GOPATH/src
对应路径下。 - 版本管理缺失 :
GOPATH
本身没有内置的版本控制机制。开发者通常需要借助第三方工具(如dep
,glide
等)或者手动将依赖复制到项目下的vendor
目录来进行版本锁定,但这并非语言内置功能,且容易导致不一致。 - 可复现性问题 :由于
go get
默认拉取最新代码,不同时间、不同环境下构建同一个项目可能会因为依赖版本的变化而产生不同的结果或构建失败。
Go 1.11 的 Go Modules 模式
Go Modules 的引入改变了这一切:
- 项目位置自由 :项目可以放在文件系统中的任何位置,不再受
GOPATH
限制。 go.mod
文件 :每个模块 (module) 的根目录下都有一个go.mod
文件。该文件定义了模块路径(module path)、项目所需的最低 Go 版本以及所有直接依赖项及其要求的最低版本(通过require
指令)。go.sum
文件 :伴随go.mod
生成的go.sum
文件包含了模块所依赖的包(包括直接和间接依赖)的具体版本及其内容的哈希校验和,用于保证依赖包的完整性和构建的可复现性。- 自动依赖管理 :当你在代码中
import
一个新的包,或者使用go build
,go test
,go list
等命令时,Go 工具链会自动分析go.mod
文件,下载缺失的依赖项到$GOPATH/pkg/mod
目录下(这是一个全局共享的缓存),并可能更新go.mod
和go.sum
文件。
示例对比
假设我们要创建一个简单的项目,依赖 rsc.io/quote
包。
Go 1.10 (GOPATH
模式)
- 确保你的项目在
$GOPATH/src
下,比如$GOPATH/src/myproject
。 - 创建
main.go
:
go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
- 获取依赖:
bash
go get rsc.io/quote
这会将 rsc.io/quote
的最新代码下载到 $GOPATH/src/rsc.io/quote
。没有版本信息被记录在你的项目里。
- 构建:
bash
go build
Go 1.11 (Go Modules 模式)
- 在任何你喜欢的位置创建项目目录,比如
/path/to/myproject
(无需在GOPATH
内)。 - 进入目录并初始化模块:
bash
cd /path/to/myproject
go mod init myproject
这会创建一个 go.mod
文件,内容类似:
txt
module myproject
go 1.11
- 创建
main.go
(内容同上)。 - 构建或运行:
bash
go build
# 或者 go run .
Go 工具会自动检测到 import "rsc.io/quote"
,查找该包的最新版本,下载它,并更新 go.mod
和 go.sum
。
go.mod
文件可能变为:
txt
module myproject
go 1.11
require rsc.io/quote v1.5.2 // 版本号可能不同
go.sum
文件也会被创建,包含类似以下的校验和信息:
txt
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO...
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq...
rsc.io/quote v1.5.2 h1:w5fc...
rsc.io/quote v1.5.2/go.mod h1:Lz...
rsc.io/sampler v1.3.0 h1:7u...
rsc.io/sampler v1.3.0/go.mod h1:T1...
现在,项目的依赖及其精确版本都被清晰地记录下来了。
GO111MODULE
环境变量
Go 1.11 使用 GO111MODULE
环境变量来控制模块支持的开启状态:
GO111MODULE=on
: 强制使用模块支持,忽略GOPATH
。GO111MODULE=off
: 强制禁用模块支持,回归GOPATH
模式。GO111MODULE=auto
(默认值): 当项目在$GOPATH/src
之外,并且根目录或任何父目录包含go.mod
文件时,启用模块支持。否则,使用GOPATH
模式。
其他相关变更
- 导入路径限制 : 由于
@
符号在模块相关的命令(如go get example.com/[email protected]
)中有特殊含义,Go 1.11 开始禁止在import
路径中使用@
符号。 - 包加载 API : 新增了
golang.org/x/tools/go/packages
包,提供了一个更强大的 API 来定位和加载 Go 源代码包,它能更好地支持模块,并可以与 Bazel、Buck 等其他构建系统集成。这个包旨在未来替代标准库中的go/build
包。 - 构建缓存强制化 : Go 1.10 引入了构建缓存 (
GOCACHE
)。Go 1.11 宣布这将是最后一个允许通过设置GOCACHE=off
来禁用缓存的版本。从 Go 1.12 开始,构建缓存将是强制性的,这是逐步淘汰$GOPATH/pkg
的一步。模块和新的包加载机制已经依赖于启用构建缓存。
总而言之,Go 1.11 的 Go Modules 为 Go 生态带来了现代化的依赖管理解决方案,虽然在当时是初步引入,但它奠定了未来 Go 项目开发的基础,极大地改善了版本控制和构建复现性。