跳转至

开发环境

GOPATH和GO MODULES

GOPATH: 想象一个巨大的公共图书馆. 所有的书 (项目代码和依赖) 都必须按照一个非常严格的系统存放在这个图书馆里. 你要写新书 (新项目), 必须去图书馆里指定的房间 ($GOPATH/src) 写. 你需要参考别人的书 (依赖), 就从图书馆的书架上拿. 问题: 如果两个项目需要同一本书的不同版本 (比如第1版和第2版), 图书馆只有一个位置放这本书, 就会产生冲突. 一个项目能用, 另一个就可能出错了.

Go Modules: 想象每个项目都是一个独立的书房. 每个书房里都有一个私人的小书架. 当你的项目需要一本书时, 你会去中央仓库"复印"一本, 然后放到你自己的小书架上. 你的书房里有一张清单 (go.mod), 上面精确记录了你需要的是哪本书的哪个版本 (例如: 第1.14.2版). 这样, 你的项目A可以用第1版, 项目B可以用第2版, 它们在各自的书房里, 互不干扰.

特性 GOPATH (旧方式, 不再推荐) Go Modules (现代方式, 主流)
核心理念 集中式工作区. 所有代码共享一个巨大的空间. 去中心化, 项目独立. 每个项目都是一个自包含的模块.
项目位置 **必须**存放在 $GOPATH/src 目录下. 非常死板. 可以存放在**任何位置**. 自由灵活.
版本管理 没有版本管理. go get 总是拉取依赖的最新代码, 无法锁定版本. 精确的版本管理. go.mod 文件记录了每个依赖的精确版本号.
依赖存放 全部存放在 $GOPATH/src. 多个项目共享同一份依赖代码. 依赖下载后缓存在 $GOPATH/pkg/mod, 但每个项目根据自己的 go.mod 文件"引用"特定版本, 逻辑上是隔离的.
如何定义项目 依靠在$GOPATH/src下的目录路径. 依靠项目根目录下的 go.mod 文件.
构建可靠性 . 任何依赖的更新都可能导致你的项目构建失败. 高 (可复现构建). 只要 go.modgo.sum 文件不变, 任何人在任何时间构建, 得到的结果都是完全一样的.
典型命令 go get <package> go mod init, go mod tidy, go get <package@version>

GOPATH和GO MODULES的工作方式

  1. 你设置一个环境变量 GOPATH (例如 ~/go).
  2. 你的所有项目代码, 比如 my-project, 必须放在 ~/go/src/github.com/my-name/my-project.
  3. 当你 go get 一个依赖, 例如 github.com/gin-gonic/gin, 它的代码会被下载到 ~/go/src/github.com/gin-gonic/gin.
  4. 你的项目和别人的项目都引用这同一个 gin 库.

主要缺点: 版本冲突和环境污染.

  1. 你在任何地方创建一个项目目录, 例如 ~/Desktop/my-new-project.
  2. 进入该目录, 运行 go mod init github.com/my-name/my-new-project. 这会创建一个 go.mod 文件, 标志着这里是一个独立的Go模块.
  3. 在你的代码中 import "github.com/gin-gonic/gin".
  4. 运行 go mod tidygo build. Go会自动:
    • 找到 gin 库的最新稳定版本.
    • 将版本号 (例如 v1.9.1) 记录到你的 go.mod 文件里.
    • 下载 gin@v1.9.1 的代码并缓存起来.
    • 生成 go.sum 文件来校验代码完整性.

主要优点: 项目独立, 版本锁定, 构建可复现.

版本管理

  • 主角: go.mod (用于版本选择) 和 go.sum (用于安全验证)
  • 流程: 当你运行 go build, go testgo mod tidy 时:
    1. Go命令首先读取 go.mod 来确定需要哪些依赖以及它们的 最低版本.
    2. Go使用一个叫做 "最小版本选择" (Minimal Version Selection) 的算法来决定最终要下载哪个版本.
    3. 下载完模块后, Go计算其哈希值.
    4. Go在 go.sum 文件中查找该模块和版本的哈希记录, 并进行 比对. 如果匹配, 继续. 如果不匹配, 报错.
  • 结论: go.sum 是一个安全 校验列表 (Checksum List). Go 的首要目标是根据 go.mod 的规则来选择版本, 然后用 go.sum 来验证下载内容的真伪.

main和init函数

init 函数

init 函数是 Go 语言中一个非常特殊的函数, 主要用于**程序执行前对包(package)进行初始化**.

主要特点:

  1. 自动执行: init 函数会在 main 函数执行之前, 由 Go 运行时自动调用. 你不能手动调用它.
  2. 多重定义:
    • 同一个包(package)可以有多个 init 函数.
    • 同一个源文件中也可以有多个 init 函数.
  3. 无参数无返回: 和 main 函数一样, init 函数不能有任何参数和返回值.
  4. 执行顺序:
    • 在同一个文件中, init 函数按其定义的顺序从上到下执行.
    • 在同一个包的不同文件中, Go 会按照文件名的字典顺序来执行 init 函数.
    • 如果包之间有依赖关系, 会先执行被依赖的包的 init 函数. 例如, 如果 main 包导入了 pkgA, 那么会先执行 pkgAinit 函数, 再执行 main 包的 init 函数.

main 函数

main 函数是 Go 语言程序的**入口点**, 是程序的开始.

主要特点:

  1. 程序入口: 每个可执行的 Go 程序都必须有一个 main 包, 并且该包中必须包含一个 main 函数.
  2. 唯一性: 在一个程序中, main 函数只能有一个.
  3. 无参数无返回: func main() 的定义是固定的, 不能有参数和返回值.
  4. 最后执行: main 函数在所有相关的 init 函数都执行完毕后才会开始执行.

initmain 的异同点

  • 相同点:
    • 都由系统自动调用.
    • 都不能有参数和返回值.
  • 不同点:
    • init 函数可以定义在任何包中, 且可以有多个.
    • main 函数只能定义在 main 包中, 并且只能有一个.
    • init 函数在 main 函数之前执行.

总的来说, init 函数是为程序运行准备环境的, 而 main 函数则是程序的主体和入口.

Go语言命令

  • go build: 编译指定的源码文件或代码包及其依赖项.
  • go clean: 删除执行其他命令时产生的临时文件和目录.
  • go doc: 显示代码包或符号的文档.
  • go env: 打印Go语言相关的环境信息.
  • go fix: 修正指定代码包中所有Go源码文件的旧版本代码到新版本.
  • go fmt: 格式化指定代码包的源码文件(使用gofmt工具).
  • go generate: 通过处理源码生成Go文件.
  • go get: 下载并安装指定的代码包及其依赖项.
  • go install: 编译并安装指定的代码包及其依赖项.
  • go list: 列出指定的代码包信息.
  • go run: 编译并直接运行指定的命令源码文件.
  • go test: 对Go程序进行测试.
  • go tool: 运行指定的go工具, 例如 go tool pprof 用于性能分析.
  • go version: 打印已安装的Go版本.
  • go vet: 一个简单的工具, 用于检查Go源码中的静态错误.