Z.S.K.'s Records

golang学习(package与module)

module其实是在go1.12才有的产物,在这之前都是由GOPATH来做包管理, GOPATH注定是要退出历史,这里来说一说module跟package的关系.

通常一个项目(Project)会根据功能拆分很多模块(module), golang 的所有文件都需要指定其所在的包(package)

package

一个包可以分成多个文件, 在编译的时候会被组合成一个文件.

包名跟所在的目录的名字可以不一样(跟文件本身的名字更没关系), 但同一个目录下的所有文件的包名字必须相同

比如这样, second目录下有两个go文件, others.go, second.go, 同时有一个third目录

1
2
3
4
5
second
third
third.go
others.go
second.go

cat others.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package others // 正确的为package api

import (
"fmt"
)

// Mainxx public
func Mainxx() {
fmt.Println("Mainxx")
}

func mainxx() {
fmt.Println("mainxx")
}

cat second.go

1
2
3
4
5
6
7
8
9
10
11
package api

import (
"fmt"
)

// Mapi public
func Mapi() {
Mainxx() //组成包的不同文件相互之间可以直接引用变量和函数,不论是否导出
fmt.Println("Mapi")
}

在second.go中声明的包名为api, 这个时候会提示如下错误, 说两个文件的包名不一致.

can't load package: package govars/second: found packages others (others.go) and api (second.go) in /Users/zhoushuke/git_uni/zhoushuke.github.com/learn-golang/golang-vars/second

因此可以把两个文件都改成package api 保持一致即可, 这里也说明了包名跟目录名可以不同

在main函数中可以这样调用

1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
"govars/second" //govars是模块名, 由go mod init时指定
)
func main() {
api.Mainxx()
api.Mapi()
}

因此: 可以这样理解,import 的是 path(路径),那么go就去那个路径下搜索,搜索当然是查找包名。只不过通常习惯是 文件名和 包名一致

假如现在third.go的文件也被声明为package api

cat third/third.go

1
2
3
4
5
6
7
8
9
10
package api

import (
"fmt"
)

// Mapi public
func Third() {
fmt.Println("third")
}

second.go也是package api, 这两者是不会冲突的, 因为third.go是在third目录下, second.go在second目录下,

但是在main函数中调用就会有问题

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
"govars/second" //govars是模块名, 由go mod init时指定
"govars/second/third"
)
func main() {
api.Mapi()
api.Third() // 这里本想调用third.go中的third, 但是都是引用包api, 这时编译器就无法区分是调用哪个了
}

解决方法就是在import时,为导入的包添加别名

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
second "govars/second" //在这里添加别名
third "govars/second/third"
)
func main() {
second.Mapi()
third.Third() // 使用别名引用
}

module

在go1.11之后开始支持module, 在1.13后全面铺开,当然也是兼容GOPATH方式

在module的方式之下的代码组织结构会有些不同.

这里主要理使用go mod引用本地包的问题.

最终目录结构:

1
2
3
4
5
6
7
8
9
10
learn-golang/golang-mod
first
first.go
go.mod
second
second.go
go.mod
go.sum
cmd
main.go

生成以上目录的命令如下:

1
2
3
4
5
cd learn-golang/golang-mod
go mod init github.com/learn-golang/golang-mod
mkdir -p learn-golang/golang-mod/{first,second}
cd learn-golang/golang-mod/first
go mod init github.com/learn-golang/golang-mod/first

这里go.sum文件暂时不管, go.sum是一个构建状态跟踪文件。它会记录当前module所有的顶层和间接依赖,以及这些依赖的校验和,从而提供一个可以100%复现的构建过程并对构建对象提供安全性的保证

代码都是非常简单, first.go代码如下:

1
2
3
4
5
6
7
8
9
package first

import (
"fmt"
)

func First() {
fmt.Println("First")
}

second.go代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
package second

import (
"fmt"

"github.com/sirupsen/logrus"
)

func Second() {
fmt.Println("second")
logrus.Info("logrus.info")
}

main.go代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"

"github.com/learn-golang/golang-mod/first"
"github.com/learn-golang/golang-mod/second"
)

func main() {
fmt.Println("start")
first.First()
second.Second()
}

生成的first目录下的go.mod文件如下:

由于second.go中引用了logrus, 因此在run的时候会进行下载

最终生成的second目录下的go.mod文件如下:

而在main.go, 引用了first及second, 如果不在最外层的go.mod中使用replace来指定到本地的话, 那go 会直接通过这个地址进行下载, 那当然会报错

因此需要在最外层的go.mod中使用replace, 指定到本地的first及second目录

从上面可以看出, 虽然second.go中使用了logrus, 但在最外层的go.mod中并没有require logrus,因为main.go中并没有用到, 如果在main.go中使用了logrus, 则在go.mod中也会出现.

运行结果如下:

参考文章:

转载请注明原作者: 周淑科(https://izsk.me)

 wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?