前言
一开始学 Go,非常晕的就是 package 包的问题,我们知道 Go 有一个很简单的文件可见性规则:首字母大写即对外可见,但是不知道对外是对什么外。
以及我在一个 ch1 目录下,创建一个 demo.go 文件,在输入 package 时, VS 又会自动提示 package ch1,这个和 main 又有什么关系呢?
我在一个 ch1 目录下写了一个 demo.go 文件,里面有一个函数,我在 ch2/demo2.go 里面想用又该怎么 import 呢?
带着这些疑问,这次来系统的了解一下~
Go的环境变量
在我们安装完 go 之后,输入
go env
会输出一大堆信息,其中 GOPATH 就是我们的工作区,我们写了一个 demo.go 文件,其中如果 import 了一些第三方库,运行的时候就会先从 GOROOT 中找,如果没有找到会再去 GOPATH 里面找
我们可以通过修改 GOPATH 添加额外的工作区,在你的 .zshrc / .bash_profile 文件里面
export GOPATH="/Users/lijun/go:/Users/lijun/Documents/Go/golearning"
引入第三方库
一般我们通过
go get -u github.com/julienschmidt/httprouter
这个指令下载的第三方库都会保存在 GOPATH 第一个工作区指定的 src 目录下
只需要在项目里
import "github.com/julienschmidt/httprouter"
就能使用这个第三方包了
有时候 go get 下载失败,可以手动去 GitHub 下载源代码,放到对应目录下,记住路径要和 GitHub 上的文件路径一致
在当前文件使用另一个文件定义的函数或变量
- 同级目录
在class文件夹下新建 demo.go 和 demo_lib.go
// demo_lib.go
package main
import "fmt"
func hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
// demo.go
package main
import "flag"
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Parse()
hello(name)
}
当在一个同级目录下面,有一个 package main 实现了 main() 函数的时候,这是作为程序的唯一入口。在同级目录下,别的文件也必须 package main,且不能再实现 main() 函数,否则提示 redeclared 错误。
此时 demo_lib.go 文件里的变量和函数对 demo.go 都是可见的。且不受可见性影响
- 不同级目录
现在我们在 class 文件夹下新建 lib 文件夹,在新建一个 demo2_lib.go 文件,demo2_lib 现在就有两种可能了- package main 并且实现 main() 函数,那么它就是另一个新程序的入口了,内部的函数和变量就不能被别的文件 import
- package 包名,这样的话是可以被外部使用,但是我们需要把首字母大写,对外部可见(这里了解了对外部是指对别的包而言),我们现在测试这种情况
package lib
import "fmt"
func Hello(name string) {
fmt.Println("Hello, %s!\n",)
}
如果 demo.go 要使用 demo2_lib 下的 Hello 函数,需要导入这个包。这时候我们需要 go install 一下
go install class/lib
后面的路径是要导入的包的相对路径,执行完之后,会在我们的工作区下生成 pkg/darwin_amd64/class/lib.a
在 demo.go 导入即可
package main
import (
"class/lib"
"flag"
)
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Parse()
lib.Hello(name)
}
到这里我们算是完成了一个供外部调用的包的程序
这里有个疑问
我在 class/lib 文件夹下创建的 demo2_lib.go 文件,并且 package lib,package的包名和所在文件夹名有必然联系吗?
// demo2_lib.go
package lib2
import "fmt"
func Hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
改成 lib2 试一下
go install 后面跟的是路径,所以依然是 class/lib
go install class/lib
在主函数什么都不改变,调用
go run demo.go
此时提示
# command-line-arguments
./demo.go:4:2: imported and not used: "class2/lib" as lib2
./demo.go:18:2: undefined: lib
从字面上理解一下的话,第一个是说导入了但是没有把 class2/lib 当做 lib2 来使用,第二行说没有定义的 lib
emmm~~~
我记得 import 包的时候可以起个别名的,试一下
package main
import (
lib2 "class/lib"
"flag"
)
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Parse()
lib2.Hello(name)
}
继续运行一下,输出结果
lijun:class2/ $ go run demo.go
Hello, everyone!
可以了!!!
想一下,我们主要做了什么,和之前有什么不同
在路径 class/lib 下,demo2_lib.go 文件没有 package lib,而是 package lib2
// 原来
package lib
// 现在
package lib2
go install class/lib 这个步骤没有变也变不了。
最后在 main 函数中调用,原来是
import "class/lib"
lib.Hello(name)
现在是
import lib2 "class/lib"
lib2.Hello(name)
也就是说,当我们要在一个项目里新建一个 tool 文件夹放我们的工具包,我们可以不 package tool,而是可以写一个别的包名,依然可以使用,只不过在使用的时候,import 的还是 pkg 下的路径,但是要给这个 import 设置它真正的包名,即这里的 lib2
总结
算是弄明白了 Go 下的文件引用,以及 GOPATH 的设置。
同一级目录下只能有一个 main,且别的文件对它都是可见的,不需要设置大小写都可以被访问到,不同级目录,根据首字母大小写可见,还需要 go install一下,再 import 对应路径,包名可以和路径不一致,但是要设置别名。
简单了解后,如果还有别的疑惑欢迎大家一起探讨!