Xavier's Blog
做更美好的自己
Go 了解包
了解包
Go 包与其他编程语言中的库或模块类似。 你可以打包代码,并在其他位置重复使用它。 包的源代码可以分布在多个 .go
文件中。 到目前为止,我们已编写 main
包,并对其他本地包进行了一些引用。
在本节中,你将了解什么是包。 你还将了解如何创建一个包,以及如何使用外部包。
main 包
你可能注意到,在 Go 中,甚至最直接的程序都是包的一部分。 通常情况下,默认包是 main
包,即目前为止一直使用的包。 如果程序是 main
包的一部分,Go 会生成二进制文件。 运行该文件时,它将调用 main()
函数。
换句话说,当你使用 main
包时,程序将生成独立的可执行文件。 但当程序非是 main
包的一部分时,Go 不会生成二进制文件。 它生成包存档文件(扩展名为 .a 的文件)。
按照约定,包名称与导入路径的最后一个元素相同。 例如,若要导入 math/rand
包,需要按如下方式将其导入:
import "math/rand"
若要引用包中的对象,请执行如下操作:
rand.Int()
让我们创建包。
创建包
在名为 calculator
的 $GOPATH/src
目录中创建新目录。 创建名为 sum.go
的文件。 树目录应如下列目录所示:
src/
calculator/
sum.go
用包的名称初始化 sum.go
文件:
package calculator
你现在可以开始编写包的函数和变量。 不同于其他编程语言,Go 不会提供 public
或 private
关键字,以指示是否可以从包的内外部调用变量或函数。 但 Go 须遵循以下两个简单规则:
- 如需将某些内容设为专用内容,请以小写字母开始。
- 如需将某些内容设为公共内容,请以大写字母开始。
接下来,让我们将以下代码添加到我们要创建的计算器包:
package calculator
var logMessage = "[LOG]"
// Version of the calculator
var Version = "1.0"
func internalSum(number int) int {
return number - 1
}
// Sum two integer numbers
func Sum(number1, number2 int) int {
return number1 + number2
}
让我们看一下该代码中的一些事项:
- 只能从包内调用
logMessage
变量。 - 可以从任何位置访问
Version
变量。 建议你添加注释来描述此变量的用途。 (此描述适用于包的任何用户。) - 只能从包内调用
internalSum
函数。 - 可以从任何位置访问
Sum
函数。 建议你添加注释来描述此函数的用途。
若要确认一切正常,可在 calculator
目录中运行 go build
命令。 如果执行此操作,请注意系统不会生成可执行的二进制文件。
创建模块
已将计算器功能组合到包中。 现在可以将包组合到模块中。 为什么? 包的模块指定了 Go 运行已组合代码所需的上下文。 此上下文信息包括编写代码时所用的 Go 版本。
此外,模块还有助于其他开发人员引用代码的特定版本,并更轻松地处理依赖项。 另一个优点是,我们的程序源代码无需严格存在于 $GOPATH/src
目录中。 如果释放该限制,则可以更方便地在其他项目中同时使用不同包版本。
因此,若要为 calculator
包创建模块,请在根目录 ($GOPATH/src/calculator
) 中运行以下命令:
输出
go mod init github.com/myuser/calculator
运行此命令后,github.com/myuser/calculator
就会变成包的名称。 在其他程序中,你将使用该名称进行引用。 命令还会创建一个名为 go.mod
的新文件。 最后,树目录现会如下列目录所示:
src/
calculator/
go.mod
sum.go
该文件的 go.mod
内容应该如下代码所示: (Go 版本可能不同。)
module github.com/myuser/calculator
go 1.14
若要在其他程序中引用此包,需要使用模块名称进行导入。 在这种情况下,其名称为 github.com/myuser/calculator
。 现在,让我们看一下示例,了解如何使用此包。
备注 过去,管理 Go 中的依赖项并不容易。 依赖关系管理系统仍在进行中。 如果你想要了解有关模块的详细信息,请参阅 Go 博客中发布的系列帖子。
引用本地包(模块)
现在,让我们先使用包。 我们将继续使用我们一直使用的示例应用程序。 这一次,我们将使用之前在 calculator
包中创建的函数,而不是 main
包中的 sum
函数。
树文件结构现应如下所示:
src/
calculator/
go.mod
sum.go
helloworld/
main.go
我们会将此代码用于 $GOPATH/src/helloworld/main.go
文件:
package main
import "github.com/myuser/calculator"
func main() {
total := calculator.Sum(3, 5)
println(total)
println("Version: ", calculator.Version)
}
请注意,import
语句使用所创建包的名称:calculator
。 若要从该包调用 Sum
函数,你需要将包名称指定为 calculator.Sum
。 最后,你现还可访问 Version
变量。 请按调用以下内容:calculator.Version
。
如果立即尝试运行程序,它将不起任何作用。 你需要通知 Go,你会使用模块来引用其他包。 为此,请在 $GOPATH/src/helloworld
目录中运行以下命令:
go mod init helloworld
在上述命令中,helloworld
是项目名称。 此命令会创建一个新的 go.mod
文件,因此,树目录会如下所示:
src/
calculator/
go.mod
sum.go
helloworld/
go.mod
main.go
如果打开 go.mod
文件,则应看到类似于下面代码的内容: (Go 版本可能不同。)
module helloworld
go 1.14
由于你引用的是该模块的本地副本,因此你需要通知 Go 不要使用远程位置。 因此,你需要手动修改 go.mod
文件,使其包含引用,如下所示:
module helloworld
go 1.14
require github.com/myuser/calculator v0.0.0
replace github.com/myuser/calculator => ../calculator
replace
关键字指定使用本地目录,而不是模块的远程位置。 在这种情况下,由于 helloworld
和 calculator
程序在 $GOPATH/src
中,因此位置只能是 ../calculator
。 如果模块源位于不同的位置,请在此处定义本地路径。
使用以下命令运行程序:
go run main.go
系统应输出如下内容:
8
Version: 1.0
挑战 1:
如果尝试从主应用程序中的 calculator
包调用 logMessage
变量或 internalSum
函数,会发生什么? 它是否运行? 试一试吧!
质询解决方案:
> package main > > import "github.com/myuser/calculator" > > func main() { > total := calculator.internalSum(5) > println(total) > println("Version: ", calculator.logMessage) > } > ``` ## 发布包 你可以轻松发布 Go 包。 只需公开提供包源代码即可实现。 大多数开发人员都使用 `GitHub` 公开发布包。 这就是为什么有时会在 `import` 语句中找到对 `github.com` 的引用。 例如,如果想要将 `calculator` 包发布到 `GitHub` 帐户,则需要创建一个名为 `calculator` 的存储库。 URL 应与下述网址类似: ```输出 https://github.com/myuser/calculator
你将通过标记存储库来对包进行版本化,如下所示:
git tag v0.1.0
git push origin v0.1.0
如果是想要使用你的包的开发人员(包括你自己)引用如下所述内容:
import "github.com/myuser/calculator"
让我们更详细地讨论如何引用第三方包。
引用外部(第三方)包
有时,程序需要引用其他开发人员编写的包。 你通常可以在 GitHub
上找到这些包。 不论你是要开发包(非 main
包)还是独立的程序(main
包),以下有关引用第三方包的说明均适用。
让我们添加对 rsc.io/quote
包的引用:
package main
import (
"github.com/myuser/calculator"
"rsc.io/quote"
)
func main() {
total := calculator.Sum(3, 5)
println(total)
println("Version: ", calculator.Version)
println(quote.Hello())
}
如果使用 Visual Studio Code,则保存文件时将更新 go.mod
文件。 现在它的外观如下所示:
module helloworld
go 1.14
require (
github.com/myuser/calculator v0.0.0
rsc.io/quote v1.5.2
)
replace github.com/myuser/calculator => ../calculator
请注意 rsc.io/quote
如何引用特定包版本。 如果需要升级程序的依赖项,则需要在此处更改版本。
使用以下命令再次运行程序:
go run main.go
输出应如下所示:
8
Version: 1.0
Hello, world.
日后对第三方包的所有引用都需要包含在 go.mod
文件中。 运行或编译应用程序时,Go 将下载其所有依赖项。