Go语言圣经 .9.包和工具

包和工具

包简介

  • 每个包一般都定义了一个不同的名字空间用于它内部的每个标识符的访问
  • 每个包还通过控制包内名字的可见性和是否导出来实现封装特性
  • 当我们修改了一个源文件,我们必须重新编译该源文件对应的包和所有依赖该包的其他包

导入路径

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

是字符串。

import ( 
"fmt"
"math/rand"
"encoding/json"
"golang.org/x/net/html"
"github.com/go-sql-driver/mysql"
)

包声明

在每个Go语言源文件的开头都必须有包声明语句。包声明语句的主要目的是确定当前包被其它包导入时默认的标识符(也称为包名)。

通常来说,默认的包名就是包导入路径名的最后一段.

关于默认包名一般采用导入路径名的最后一段的约定也有三种例外情况。

  1. 包对应一个可执行程序,也就是main包,这时候main包本身的导入路径是无关紧要的
  2. 包所在的目录中可能有一些文件名是以 test.go 为后缀的Go源文件(前面必须有其他字符, 因为以 或 . 开头的源文件会被构建工具忽略),并且这些源文件声明的包名也是以 _test 为后缀名的。这种目录可以包含两种包:一种是普通包,另一种则是测试的外部扩展包
  3. 一些依赖版本号的管理工具会在导入路径后追加版本号信息,例如"gopkg.in/yaml.v2"。这种情况下包的名字并不包含版本号后缀,而是yaml。

导入声明

import "fmt" 
import "os"

import (
"fmt"
"os"
)

如果我们想同时导入两个有着名字相同的包,例如math/rand包和crypto/rand包,那么导入声明必须至少为一个同名包指定一个新的包名以避免冲突。这叫做导入包的重命名。

import ( 
"crypto/rand"
mrand "math/rand" // alternative name mrand avoids conflict
)

包的匿名导入

如果只是导入一个包而并不使用导入的包将会导致一个编译错误。但是有时候我们只是想利用导入包而产生的副作用:它会计算包级变量的初始化表达式和执行导入包的init初始化函数。这时候我们需要抑制“unused import”编译错误,我们可以用下划线 _ 来重命名导入的包。像往常一样,下划线 _ 为空白标识符,并不能被访问。

import _ "image/png" // register PNG decoder
import ( 
"database/sql"
_ "github.com/lib/pq" // enable support for Postgres
_ "github.com/go-sql-driver/mysql" // enable support for MySQL
)

db, err = sql.Open("postgres", dbname) // OK
db, err = sql.Open("mysql", dbname) // OK
db, err = sql.Open("sqlite3", dbname) // returns error: unknown driver "sqlite3"

包和命名

工具

工作区结构

对于大多数的Go语言用户,只需要配置一个名叫GOPATH的环境变量,用来指定当前工作目录即可。当需要切换到不同工作区的时候,只要更新GOPATH就可以了。例如,我们在编写本书时将GOPATH设置为 $HOME/gobook

GOPATH对应的工作区目录有三个子目录:

  • src: 存储源代码
  • pkg: 编译后的包的目标文件
  • bin: 用于保存编译后的可执行程序

量GOROOT用来指定Go的安装目录

下载包

通常的解决方案是使用vendor的目录用于存储依赖包的固定版本的源代码,对本地依 赖的包的版本更新也是谨慎和持续可控的

构建包

go build 命令编译命令行参数指定的每个包。如果包是一个库,则忽略输出结果;这可以用于检测包是可以正确编译的。如果包的名字是main, go build 将调用链接器在当前目录创建一个可执行程序;以导入路径的最后一段作为可执行程序的名字。

包文档

内部包

Go语言的构建工具对包含internal名字的路径段的包导入路径做了特殊处 理。这种包叫internal包,一个internal包只能被和internal目录有同一个父目录的包所导入。例 如,net/http/internal/chunked内部包只能被net/http/httputil或net/http包导入,但是不能被net/url包导入。不过net/url包却可以导入net/http/httputil包。

net/http 
net/http/internal/chunked
net/http/httputil
net/url

查询包

go list 命令可以查询可用包的信息。其最简单的形式,可以测试包是否在工作区并打印它的导入路径:

$ go list github.com/go-sql-driver/mysql 
github.com/go-sql-driver/mysql
$ go list ... 
archive/tar
archive/zip
bufio bytes
cmd/addr2line
cmd/api
...many more...

或者是特定子目录下的所有包:

或者是和某个主题相关的所有包:

$ go list ...xml... 
encoding/xml
gopl.io/ch7/xmlselect

go list 命令还可以获取每个包完整的元信息,而不仅仅只是导入路径,这些元信息可以以不同格式提供给用户。其中 -json 命令行参数表示用JSON格式打印每个包的元信息。