Go 学习笔记(十)- 包和工具

现在的程序开发都基于各种包或模块,可能一个程序我们写几行代码即可,其他功能都可以通过开源的包来实现。
但是 go 的包感觉也是比较烂的,不像 node 的 npm 那样 semver(语义化版本) 管理模块,而是通过 git 手动管理。

包简介

Go语言的闪电般的编译速度主要得益于三个语言特性。
第一点,所有导入的包必须在每个文件的开头显式声明,这样的话编译器就没有必要读取和分析整个源文件来判断包的依赖关系。
第二点,禁止包的环状依赖,因为没有循环依赖,包的依赖关系形成一个有向无环图,每个包可以被独立编译,而且很可能是被并发编译。
第三点,编译后包的目标文件不仅仅记录包本身的导出信息,目标文件同时还记录了包的依赖关系。

导入路径

每个包是由一个全局唯一的字符串所标识的导入路径定位。出现在import语句中的导入路径也是字符串。

1
2
3
4
5
6
7
8
9
import (
"fmt"
"math/rand"
"encoding/json"

"golang.org/x/net/html"

"github.com/go-sql-driver/mysql"
)

包声明

在每个Go语音源文件的开头都必须有包声明语句。包声明语句的主要目的是确定当前包被其它包导入时默认的标识符(也称为包名)。
例如,math/rand 包的每个源文件的开头都包含 package rand 包声明语句,所以当你导入这个包,你就可以用 rand.Intrand.Float64 类似的方式访问包的成员。

导入声明

简单明了。

1
2
3
4
5
6
7
8
9
10
11
12
import "fmt"
import "os"

import (
"fmt"
"os"
)

import (
"crypto/rand"
mrand "math/rand" // 命名导入
)

包的匿名导入

如果导入的包未使用,会导致编译错误,需要使用匿名导入来抑制错误。

1
import _ "image/png"

包和命名

go 包推荐简短命名,但是不要和常用的变量名重名。

1
2
3
4
package rand // "math/rand"

type Rand struct{ /* ... */ }
func New(source Source) *Rand

例如这样的,数据类型描述,可以用 New 函数来实例化类型。

工具

go 提供了很多工具,控制台输入 gogo help 查看。

工作区结构

环境变量 GOPATH 是指 go 项目路径,例如 $HOME/gobook
环境变量 GOROOT 是指 go 安装目录,例如 /usr/local/go

下载包

通过 go get 包名 来获取包。

构建包

编译包或应用:

1
$ go build gopl.io/ch1/helloworld

安装包或应用:

1
$ go install gopl.io/ch1/helloworld

跨平台编译:

1
2
3
$ GOOS=darwin GOARCH=amd64 go build # 编译 mac 下的 64位 应用
$ GOOS=linux GOARCH=amd64 go build # 编译 linux 下的 64位 应用
$ GOOS=windows GOARCH=386 go build # 编译 win 下的 32位 应用

例如 net_linux.go 或 asm_amd64.s,Go 语言的构建工具将只在对应的平台编译这些文件。
如果在包中加入 // +build linux darwin 表示该包只在 linux 和 mac 下被编译。
// +build ignore 是忽略该包。

包文档

在函数前写注释,即可通过 go doc 来查看。

1
2
$ go doc time # 查看整个包的文档
$ go doc time.Since # 查看具体函数的文档

甚至可以直接通过 web 查看所有包文档。

1
$ godoc -http :8000

内部包

Go语言的构建工具对包含internal名字的路径段的包导入路径做了特殊处理。这种包叫internal包,一个internal包只能被和internal目录有同一个父目录的包所导入。

查询包

通过 go list 包名go list ...go list ...xml... 列出包。