结束了上一章地狱般的折麽,从本章开始便重新回归胎教级别,如果说第二章的内容已经完全掌握,我觉得剩下的第3~5章基本可以略过了。
“包”的基础知识
如果连“包”的基本概念都不了解,那就先别往下看了,去学学Java的基础知识吧。本书没有过多概念性的解释,而是具体说明在GO语言中,如何定义和使用包。
1. 包的定义(命名)
包名应全部小写,每个.go文件都必须在第一行使用package <name>
声明自己属于哪个包,同一个目录下不同的go源码必须声明为同一个包。此外,不同路径下的包名是可以相同的,因为导入包时采用的是全路径,路径本身可以区分不同的包。
另外,main包
很特殊,如果一个工程内编译器没有找到main包,就不会创建可执行文件,main()
函数也必须在main包中定义。
2. 包的引用(导入)
导入方式
go语言支持远程导入、本地导入和命名导入:
- 本地导入——
import "fmt"
- 远程导入——
import “github.com/spf13/viper”
- 命名导入——
import myFmt "fmt"
本地/远程导入,都是为了能愉快地使用别人已经写好的功能,不必重新发明轮子。而命名导入主要是为了方式重名包,例如:
1 | // 两个buffer包名相同,存在冲突,调用时有歧义 |
此外,不管用哪种方式导入,编译器都会先从GO的安装路径$GOROOT中寻找,没有的话就是$GOPATH中查找。
⚠️注意:
本地导入和远程导入从本质上讲没有区别。远程导入就是先从网上把第三方库下载到GOPATH当中,当作本地库import。
GOROOT和GOPATH
一个C/C++程序员(我是说我)比较容易犯的一个概念性错误——通过相对路径导入。举个例子:
如果某个工程源码结构是这样的:
1 | project/ |
在上边👆的目录结构可以看到,packages目录和main.go文件在同一级目录下,如果习惯了C/C++的思想,会这样导入packages下面的包:
1 | import ( |
从相对路径的角度来看,这么干没问题,但我们忽略了GOROOT/GOPATH
两个环境变量,GO语言只会从根据这两个环境变量去寻找包。
GOROOT
通常是go语言安装路径,比如/usr/local/go/
GOPATH
通常是用户自定义包路径
这种套路非常类似于Java项目开发中的JAVA_HOME、CLASSPATH的概念。
需要注意一点,go编译器会自动为这两个环境后追加src目录,也就是说工程依赖的包应该放在$GOPATH/src
内。换而言之,上边的packages目录必须放置在project/src内,并将工程目录添加到GOPATH中。
3. 远程导入的坑
如果是远程导入,可以使用go get
命令,下来源码中声明的“远程包”,它还会自动下载和更新各种依赖,但由于防火<哔哔>的缘故,这个命令大概率会timeout,只能曲线救国…
说明一下原因,当使用go get -v
自动下载依赖包的时候,就可以看到整个过程,会默认去访问https://golang.org/x/<pkg>
更新这些依赖,而golang.org的背后是一家名叫Google的公司在运营,由于我个遵纪守法的好公民,在我心目中只要我听不到看不到,它就不存在,是的,从小就在书本里学到一个成语——掩耳盗铃。
所以,我很奇怪,为什么go命令行要去访问一个根本不存在的网站,可能是个bug。好在机智如我,GitHub的GO仓库其实本质上就是golang.org/x 的镜像,所以务必记住这个镜像地址:
1 | ✍️划重点✍️ |
4. 使用下划线占位符
之前已经学习到,每个包内的init
函数总是先于main
函数执行,但前提是这个包在程序中被导入了。
很多时候,我们需要执行包内的init()
函数来初始化某些资源,但我们不会去直接调用包内的任何变量。由于GO的限制,不允许导入一个根本不曾使用的包。
那么问题来了,如何导入一个包,却表面上不使用它?import _ <package_name>
是个不错的选择。
GO的工具链
对于我们这些Linux出身的猿类来说,敲命令来管理工程是非常有吸引力的,因为可以了解并掌握更多细节,自由度更高。
不论官方还是社区,go语言提供了非常丰富的命令,书中列举不是很多,我也觉得这种东西更适合直接看手册,我在此仅总结几个新手常用的,便于巩固记忆。
go
go build
构建工程go clean
清理工程go run <file.go>
运行工程源码,其实是后台自动帮你完成构建go doc <key>
命令行文档查看器,包、函数、符号等具体使用规则
gb
作为go新手,我可能还没体验过大型项目那种复杂的依赖关系,但作为nodejs、java的实践者,我很清楚依赖管理的好处。不论是npm还是maven,它们对效率的提升是巨大的。
gb源自社区,目的是解决go的第三方依赖问题。试想一下,对于一个大型项目,几十上百个第三方库,不同的版本,库与库之间的藕断丝连,一个个去筛查定位,无疑是在浪费生命。
gb其实就是在工程目录下,创建一个vendor目录,里面存放各种需要的依赖库,gb会自动下载、更新这些库文件,不用开发者操心(总之用过npm/maven的人都懂,懒得解释了)。
自己的工程目录就放在src目录下,不会和第三方的依赖有刮扯,需要构建工程的时候,仅需gb build all
,搞定!
小结一下
- GO包管理采用绝对路径,务必注意GOROOT和GOPATH环境变量
- GO有本地、远程、命名三种导入包的方式
- 远程导入可能会timeout,记得替换为GitHub的镜像仓库
- 仅需要执行包内的init函数时,可以使用下划线占位符
- go命令的build/clean/run/doc是很常用的命令工具
- 学会采用依赖管理工具来管理项目,如gb