库源码文件是不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用(只要遵从 Go 语言规范的话)。他代码”可以与被使用的程序实体在同一个源码文件内,也可以在其他源码文件,甚至其他代码包中。
cat demo4.go
package main //这里也申明了属于main包
import (
"flag"
)
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Parse()
hello(name) //调用了代码包一个叫作hello的函数
}
cat demo4_lib.go
package main //这里也申明了属于main包
import "fmt"
func hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
执行:go run demo4.go demo4_lib.go
Hello, everyone!
demo4.go 和 demo4_lib.go 都放在了一个相对路径为q1的目录中
$ ll
-rw-r--r-- 1 daixuan staff 178 11 6 22:13 demo4.go
-rw-r--r-- 1 daixuan staff 90 11 6 22:13 demo4_lib.go
$ go build .
ls
demo4.go demo4_lib.go q1//build一个q1文件
$ ./q1
Hello, everyone!
$ ./q1 -name huaihe
Hello, huaihe!
代码包声明的基本规则。这里再总结一下:
第一条规则,同目录下的源码文件的代码包声明语句要一致。也就是说,它们要同属于一个代码包。这对于所有源码文件都是适用的。
如果目录中有命令源码文件,那么其他种类的源码文件也应该声明属于main包。这也是我们能够成功构建和运行它们的前提。
第二条规则,源码文件声明的代码包的名称可以与其所在的目录的名称不同。在针对代码包进行构建时,生成的结果文件的主名称与其父目录的名称一致。
对于命令源码文件而言,构建生成的可执行文件的主名称会与其父目录的名称相同
模块化编程的方式,根据代码的功能和用途把它们放置到不同的代码包中
先搞定环境变量:
vim ~/.bash_profile
export GOPATH=/Users/daixuan/pinduoduo/go:/Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers
source ~/.bash_profile
$ echo $GOPATH
/Users/daixuan/pinduoduo/go:/Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers
查看代码包目录:
/Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers/src/puzzlers/article3/q2
daixuandeMacBook-Pro:q2 daixuan$ tree .
.
├── demo5.go
└── lib
└── demo5_lib.go
1 directory, 2 files
查看代码文件:
cat demo5.go
package main
import (
"flag"
"github.com/Golang_Puzzlers/src/puzzlers/article3/q2/lib"
)
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Parse()
lib.Hello(name)
}
cat lib/demo5_lib.go
package lib5 //注意,这里lib5应该与父目录lib对应,否则报错
import "fmt"
func Hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
执行:
go run demo5.go
# command-line-arguments
./demo5.go:6:2: imported and not used: "github.com/Golang_Puzzlers/src/puzzlers/article3/q2/lib" as lib5
./demo5.go:17:2: undefined: lib
第一个错误提示的意思是,我们导入了puzzlers/article3/q2/lib包,但没有实际使用其中的任何程序实体。这在 Go 语言中是不被允许的,在编译时就会导致失败。
注意,这里还有另外一个线索,那就是“as lib5”。这说明虽然导入了代码包puzzlers/article3/q2/lib,但是使用其中的程序实体的时候应该以lib5.为限定符。这也就是第二个错误提示的原因了。Go 命令找不到lib.这个限定符对应的代码包。
为什么会是这样?根本原因就是,我们在源码文件中声明所属的代码包与其所在目录的名称不同。请记住,源码文件所在的目录相对于 src 目录的相对路径就是它的代码包导入路径,而实际使用其程序实体时给定的限定符要与它声明所属的代码包名称对应。
有两个方式可以使上述构建成功完成。我在这里选择把 demo5_lib.go 文件中的代码包声明语句改为package lib
。理由是,为了不让该代码包的使用者产生困惑,我们总是应该让声明的包名与其父目录的名称一致。
解决办法:
vim lib/demo5_lib.go
package lib5 //注意,这里lib5修改为lib,与父目录lib对应
import "fmt"
func Hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
执行不报错:
$ go run demo5.go
Hello, everyone!
$ go run demo5.go -name huaihe
Hello, huaihe!
4.你可能会有疑问,我为什么要把 demo5_lib.go 文件中的那个函数名称hello的首字母大写?实际上这涉及了 Go 语言中对于程序实体访问权限的规则。超级简单,名称的首字母为大写的程序实体才可以被当前包外的代码引用,否则它就只能被当前包内的其他代码引用。通过名称,Go 语言自然地把程序实体的访问权限划分为了包级私有的和公开的。对于包级私有的程序实体,即使你导入了它所在的代码包也无法引用到它。
答案是肯定的。在 Go 1.5 及后续版本中,我们可以通过创建internal代码包让一些程序实体仅仅能被当前模块中的其他代码引用。这被称为 Go 程序实体的第三种访问权限:模块级私有。
具体规则是,internal代码包中声明的公开程序实体仅能被该代码包的直接父包及其子包中的代码引用。当然,引用前需要先导入这个internal包。对于其他代码包,导入该internal包都是非法的,无法通过编译。
“Golang_Puzzlers”项目的puzzlers/article3/q4包中有一个简单的示例,可供你查看。你可以改动其中的代码并体会internal包的作用。
本篇文章中详细讨论了把代码从命令源码文件中拆分出来的方法,这包括拆分到其他库源码文件,以及拆分到其他代码包。这里涉及了几条重要的 Go 语言基本编码规则,
即:代码包声明规则、代码包导入规则以及程序实体的访问权限规则。在进行模块化编程时,你必须记住这些规则,否则你的代码很可能无法通过编译。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。