go 的文件都以 package PACKAGE_NAME 开头,表示这个文件属于哪个包,如果是 package main 表示这个文件是可以运行的。同一目录下的文件的package名称都相同。
我们可以通过新建一个目录,在目录下新建若干 go 源文件的方式来创建自己的 package,每个 go 源文件的的 package 的名字都是统一的(即我们创建的包的名字),它们也都属于同一个包,不同源文件中定义的函数也都可以互相使用,并不存在需要先声明再调用的限制。在该目录下可以使用 go build 对我们的包进行编译。
在其他文件中,可以使用 import "path/to/PACKAGE_NAME"来导入包,从而引用包中的结构体、函数等等。go 不允许循环引用。使用 import ALIAS "path/to/PACKAGE_NAME" 的方式可以以别名的形式导入包。
go 也不允许导入未使用的包,如果我们需要使用某个包中的 init() 方法,但又不会使用这个包,可以使用import _ PACKAGE匿名导入。如果导入了未使用的包,编译会报错,一个比较好的方式是使用goimports工具,可以自动清除文件中未使用的导入包。对某个路径下的所有文件使用goimports的方法为:goimports -w path/
在package中,我们可以定义一系列的变量、常量、类型、结构体、方法、函数等,如果我们定义的变量、函数、etc...的名称是大写开头,则这些变量、函数对于调用这个包的外部调用者是可见的,如果是小写开头,则这些变量、函数就叫做unexported(未导出的),可以理解为私有方法,只有在包的内部才能使用这些变量、函数。这样的方式提供了安全性和更好的封装性,包的调用者不需要关心一些实现细节,而是只使用包提供的方法。
有些包中可能会需要一个初始化函数,来初始化一些全局变量,比如初始化某个map的长度,初始化一个数据库链接等等。在包中使用init()函数,则在初始化的时候,init()中的内容会自动执行。每个包都会以导入的顺序进行初始化,然后 main 函数才会开始执行。
另一种初始化包级变量的方式是手动定义可导出的初始化函数,比如 InitMap(),然后在主函数的文件的 init()函数中,手动调用这个包的 InitMap()
go 提供了自己的包管理工具 go mod,在环境变量中配置 export GO111MODULE=on 即可使用。go mod可以自动添加、删除、下载依赖,支持从常用的git仓库如GitHub下载依赖,非常易于使用。
在项目中使用 go mod init,会初始化一个go.mod文件,里面记录了项目文件中依赖的包以及对应版本。使用 go mod tidy,则会自动整理(添加、删除)依赖,包括下载依赖包,但不会更新依赖包在 go.mod中记录的版本。想要将某个依赖更新到最新版,可以使用 go get XXX。关于 go get 有几点冷知识:
如果多个依赖包中对某个特定依赖包的依赖版本不同,go mod 会选择所有依赖版本中最高的那个版本
如果想要强制指定某个依赖的版本,比如因为兼容性问题不想更新到最新版的时候,则可以在go.mod中使用replace,比如:
replace github.com/golang/protobuf => github.com/golang/protobuf v1.3.5
使用go mod graph可以查看所有依赖项之间的依赖关系,比如用go mod graph | grep XXX可以查到某个依赖项是如何被依赖的。
常见报错: