对于任何广泛使用的编程语言,乃至整个开源社区来说,在不同项目之间借用和共享代码的能力都是基础。借用代码使程序员能够将大部分时间花在针对自己的需求编写代码上,而且通常他们的一些新代码最终会对其他人有用。然后他们可能决定将这些可重用的部分组织成一个单元,并在他们的团队或更广泛的编程社区中共享它。
在Go中,可重用代码的基本单元称为包。即使最简单的Go程序也是它自己的包,并且可能使用至少一个其他包。在本教程中,你将编写两个小程序:一个使用标准库包来生成随机数,另一个使用流行的第三方包来生成UUID。然后,您可以选择编写一个较长的程序来比较两个相似的标准库包,导入并使用这两个包,即使它们具有相同的基本名称。最后,您将使用goimports
工具来查看如何格式化导入。
注意: Go中还有一个更高级别的可重用代码单元:module。模块是包的版本化集合。您将在后面的文章“如何使用Go模块”中探索模块
在开始本教程之前,您只需要安装Go。
与大多数语言一样,Go有一个内置的可重用代码库,您可以将其用于常见任务。例如,你不需要编写自己的代码来格式化和打印字符串,或者发送HTTP请求。Go标准库为这些任务和许多其他任务提供了软件包。
[如何用Go编写第一个程序]中的程序使用了标准库中的fmt
和strings
包。让我们再写一个程序,使用math/rand
包来生成一些随机数。
在nano
或者你喜欢的文本编辑器中打开一个名为random.go
的新文件:
nano random.go
下面来创建一个程序,打印5个从0到9的随机整数。将以下内容粘贴到编辑器中:
package main
import "math/rand"
func main() {
for i := 0; i < 5; i++ {
println(rand.Intn(10))
}
}
这个程序导入了’math/rand’包,并通过引用其基本名称rand
来使用它。这是出现在包中每个Go源文件顶部的package
声明中的名称。
for循环的每次迭代都会调用rand.Intn(10)
来生成一个介于0到9之间的随机整数(不包括10),然后将该整数打印到控制台。
请注意,对println()
的调用没有引用包的名称。这是一个不需要导入的内置函数。通常你会使用fmt
包中的fmt.Println()
函数,但这个程序使用println()
来引入内置函数。
保存程序。如果你使用的是nano
,按下CTRL+X
,然后Y
和ENTER
来确认你的更改。然后运行程序:
go run random.go
你将看到从0到9的5个整数:
Output1
7
7
9
1
看起来随机数生成器正常工作了,但请注意,如果你反复运行程序,它每次打印的都是相同的数,而不是你预期的新随机数。这是因为我们没有调用rand.Seed()
函数来用一个唯一的值初始化数字生成器。如果你不这样做,该包的行为就像调用了rand.Seed(1)
一样,因此它每次都会生成相同的“随机”数字。
因此,每次程序运行时,你都需要为数字生成器提供一个唯一的值。程序员通常使用以纳秒为单位的当前时间戳。为此,你需要time
包。在编辑器中再次打开random.go
并粘贴以下内容:
package main
import (
"math/rand"
"time"
)
func main() {
now := time.Now()
rand.Seed(now.UnixNano())
println("Numbers seeded using current date/time:", now.UnixNano())
for i := 0; i < 5; i++ {
println(rand.Intn(10))
}
}
当导入多个包时,可以使用括号来创建导入块。通过使用块,你可以避免在每一行上重复import
关键字,这使你的代码更简洁。
首先,你通过time.Now()
函数获取当前系统时间,该函数返回一个time
结构体。然后你将时间传递给rand.Seed()
函数。该函数需要一个64位整数(int64
),所以你需要在now
结构体上使用Time.UnixNano()
方法来传递以纳秒为单位的时间。最后,打印生成随机数所需的时间。
现在保存并再次运行程序:
go run random.go
你应该会看到类似下面的输出:
OutputNumbers seeded using current date/time: 1674489465616954000
2
6
3
1
0
如果你运行这个程序几次,每次都应该看到不同的整数,以及用于生成随机数的唯一整数。
让我们再编辑一次程序,以更友好的格式打印种子时间。编辑包含第一个println()
调用的那行代码,如下所示:
println("Numbers seeded using current date/time:", now.Format(time.StampNano))
现在你正在调用Time.Format()
方法,并传入在time
包中定义的多种格式中的一种。time.StampNano
常量(const
)是一个字符串,将它传递给Time.Format()
可以让你打印月份、日期和时间,精确到纳秒。保存并再次运行程序:
go run random.go
OutputNumbers seeded using current date/time: Jan 23 10:01:50.721413000
7
6
3
7
3
这比看到一个表示自1970年1月1日以来经过的纳秒数的巨大整数要好。
如果您的程序不需要随机整数,而是需要UUID,许多程序员使用它作为其部署中数据片段的全局唯一标识符,该怎么办?Go标准库没有生成这些内容的软件包,但社区提供了。现在让我们看看如何下载和使用第三方包。
生成uuid最流行的包之一是github.com/google/uuid
。第三方包总是通过它们的全限定名称来知道,其中包括托管代码的网站(例如github.com),开发它的用户或组织(例如谷歌),以及基本名称(例如uuid)。在导入包时,在pkg.go.dev和其他地方阅读它的文档时,您将使用它的全限定名称。但是,在代码语句中引用它时,只能使用基本名。
在下载包之前,您需要初始化一个模块,这是Go管理程序的依赖项及其版本的方式。要初始化一个模块,请使用go mod init
并传入你自己的包的全限定名称。如果你想在GitHub上以用户名“sammy”托管你的模块,可以像这样初始化模块:
go mod init github.com/sammy/random
这将创建一个名为go.mod
的文件。让我们看看这个文件:
cat go.mod