了解Go中如何使用包、变量和函数

聚合数据 · · 850 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

简介

在上一个模块中,你配置了一个 Go 环境,用于创建和运行 Go 中所编写的程序。 此外,你还了解到 Go 程序的编码结构基本知识,以及如何编译和运行应用程序。

在此模块中,你将逐步学习中 Go 提供的基本数据类型,并使用函数组织在 .go 文件中编写的代码。 你还将了解如何在同一包下的不同文件中管理和编写程序。 最后,你将了解如何在 Go 中创建和使用包。

学习目标

在此模块中,你将:

  • 声明变量和常量
  • 了解 Go 提供的基本数据类型
  • 编写函数
  • 创建和使用包

先决条件

  • 可供创建应用程序的 Go 环境
  • 能够创建和修改 .go 文件
  • 能够使用终端运行 Go 应用程序

声明和使用变量

申明变量

若要声明变量,需要使用 var 关键字:

`var firstName string` 

前一个语句声明 string 类型中的一个名为firstName 的变量。 (我们将在后续章节中讨论数据类型。)此示例演示声明变量的最基本方法。 如要声明其他变量,只需将类似的语句添加到上一个语句中。 但如果两个变量的类型相同,则可以在一行中声明多个变量:

`var firstName, lastName string` 

通过在变量名称后面添加逗号 (,),就表示你将要声明其他变量。 在这种情况下,前一个语句就声明了 string 类型中的两个变量:firstNamelastName。 如要添加 int 类型的第三个变量,请按如下所示编写代码:

`var firstName, lastName string
var age int` 

另一种编写前一个语句的方法是在 var 关键字后面使用括号,就像通过一个专用块来声明变量一样,如下所示:

`var (
    firstName, lastName string
    age int
)` 

初始化变量

此时,你只可声明了变量,但有时候,你需要为它们赋予初始值。 在 Go 中,你可以通过多种方式初始化变量。 例如,沿用前面提及的同一个示例,你可以使用此代码初始化每个变量:

`var (
    firstName string = "John"
    lastName  string = "Doe"
    age       int    = 32
)` 

如果你决定初始化某个变量,则不需要指定其类型,因为当你使用具体值初始化该变量时,Go 会推断出其类型。 例如,你可以用此方式声明并初始化变量:

`var (
    firstName = "John"
    lastName  = "Doe"
    age       = 32
)` 

Go 将推断出变量 firstNamelastName 的类型为 string,并且变量 age 属于 int 类型。

通过多种方式初始化变量

在 Go 中,你可以在单行中声明和初始化变量。 使用逗号将每个变量名称隔开,并对每个值执行相同的操作(按同一顺序),如下所示:

`var (
    firstName, lastName, age = "John", "Doe", 32
)` 

还可以通过另一种方式来声明和初始化变量。 此方法是在 Go 中执行此操作的最常见方法。 以下是我们使用的同一个示例说明:

`func main() {
    firstName, lastName := "John", "Doe"
    age := 32
    println(firstName, lastName, age)
}` 

运行上述代码,确认此方法能否声明和初始化变量。

请注意,在定义变量名称后,需要在此处加入一个冒号等于号 (:=) 和相应的值。 使用冒号等于号时,要声明的变量必须是新变量。 如果使用冒号等于号并已经声明该变量,将不会对程序进行编译。 继续尝试。

最终,你能在函数内使用冒号等于号。 在声明函数外的变量时,必须使用 var 关键字执行此操作。 如果你不熟悉函数,请不要担心。 我们会在后续单元中介绍函数。

声明常量

有时,你需要在代码中加入静态值,这称为 常量。 Go 支持使用常量。 用于声明常量的关键字是 const

例如,你可以按如下所示声明常量:

`const HTTPStatusOK = 200` 

与变量一样,Go 可以通过分配给常量的值推断出类型。 在 Go 中,常量名称通常以混合大小写字母或全部大写字母书写。

如果需要在一个块中声明多个常量,可以按如下所示执行:

`const (
    StatusOK              = 0
    StatusConnectionReset = 1
    StatusOtherError      = 2
)` 

备注: Go 为常量定义了一个有趣的概念,即 iota,本模块未对此概念作进一步解释。 但你可以查看 GitHub 上的 Go wiki 了解更多信息。 请记住,iota 是一个关键字;如果这些值存在先后顺序,Go 可使用此关键字简化常量定义。

常量和变量之间既有相似之处,也有一些重要差异。 例如,你可以在不使用常量的情况下声明常量。 你不会收到错误消息。 不能使用冒号等于号来声明常量。 如果采用这种方式,Go 会发出警告。

如果声明了变量但未使用,Go 会抛出错误

需要记住的重要一点是,在 Go 中,当你声明一个变量但不使用它时,Go 会抛出错误,而不是像某些其他编程语言一样抛出警告。

例如,让我们回到前面提及的某个示例,然后删除 println 调用:

`func main() {
    firstName, lastName := "John", "Doe"
    age := 32
}` 

你将看到以下错误消息:

`./main.go:4:2: firstName declared but not used
./main.go:4:13: lastName declared but not used
./main.go:5:2: age declared but not used` 

请记住,对于在 Go 中声明的每个变量,你必须将其用于某处。

了解基本数据类型

Go 是一种强类型语言。 这意味着你声明的每个变量都绑定到特定的数据类型,并且只接受与此类型匹配的值。

Go 有四类数据类型:

  • 基本类型:数字、字符串和布尔值
  • 聚合类型:数组和结构
  • 引用类型:指针、切片、映射、函数和通道
  • 接口类型:接口

在此模块中,我们仅介绍基本类型。 如果你不知道其他类型是什么,请不要担心。 我们将在后续模块中进行介绍。

首先,我们一起浏览数值的数据类型。

整数数字

一般来说,定义整数类型的关键字是 int。 但 Go 还提供了 int8int16int32int64 类型,其大小分别为 8163264 位的整数。 当你只使用 int 时,32 位系统上的大小为 32 位,64 位系统上则为 64 位(大多数情况下如此,不过在不同计算机上或有所不同)。 如果需要将值表示为无符号数字,则可以使用 uint,但仅当有特定原因时才使用此类型。 此外,Go 还提供 uint8uint16uint32uint64 类型。

下面的示例演示如何在 Go 中使用各种整数类型:

`var integer8 int8 = 127
var integer16 int16 = 32767
var integer32 int32 = 2147483647
var integer64 int64 = 9223372036854775807
println(integer8, integer16, integer32, integer64)` 

大多数情况下,你将使用 int,但需要了解其他整数类型,因为在 Go 中,intint32 不同,即使整数的自然大小为 32 位也是如此。 换句话说,需要强制转换时,你需要进行显式转换。 如果尝试在不同类型之间执行数学运算,将会出现错误。 例如,假定你具有下面的代码:

`var integer16 int16 = 127
var integer32 int32 = 32767
println(integer16 + integer32)` 

运行该程序时,你会收到以下错误:

`invalid operation: integer16 + integer32 (mismatched types int16 and int32)` 

正如你所见,不同于其他编程语言,在 Go 中将值从一种类型转换为另一种类型时,你需要执行显式操作。 我们将在本模块结束时讨论如何正确地强制转换类型。

在学习 Go 过程中,你可能会收到有关 runes 的信息。 rune 只是 int32 数据类型的别名。 它用于表示 Unicode 字符(或 Unicode 码位)。 例如,假设你具有以下代码:

`rune := 'G'
println(rune)` 

运行前面的代码片段时,你可能会在命令提示符下看到程序打印符 G。 不过,你还会看到数字 71,它表示 GUnicode 字符。 我们将在后续模块中详细介绍 runes。

你可以通过查看 Go 源代码来了解每种类型的范围。 了解每种类型的范围可帮助你选择正确的数据类型,并且还可避免占用内存中的位。

挑战 1:
设置 int 类型的另一个变量,并使用 integer32integer64 变量中的值来确认系统上变量的自然大小。 如果你使用的是 32 位系统并使用大于 2,147,483,647 的值,则会出现如下的溢出错误:constant 9223372036854775807 overflows int。

质询解决方案:

`package main

func main() {
   var integer32 int = 2147483648
   println(integer32)
}` 

挑战 2:
声明一个无符号变量(如 uint),并用一个负值(如 -10)对其进行初始化。 尝试运行程序时,应会出现如下错误:constant -10 overflows uint。

质询解决方案:

`package main

func main() {
   var integer uint = -10
   println(integer)
}` 

浮点数字

Go 提供两种浮点数大小的数据类型:float32float64。 如果需要存储较大的数字,则可以使用这些类型,这些类型无法适应前面提到的任何一个整数类型。 这两种类型的区别是它们可以容纳的最大位数。 查看以下行,了解如何使用这两种类型:

`var float32 float32 = 2147483647
var float64 float64 = 9223372036854775807
println(float32, float64)` 

你可以使用 math 包中提供的 math.MaxFloat32math.MaxFloat64 常量来查找这两种类型的限制。 使用以下代码打印命令提示符中的最大浮点值:

`package main

import "math"

func main() {
    println(math.MaxFloat32, math.MaxFloat64)
}` 

当需要使用十进制数时,浮点类型也很有用。 例如,你可以编写类似于以下代码的内容:

`const e = 2.71828
const Avogadro = 6.02214129e23
const Planck = 6.62606957e-34` 

请注意,与前面的代码一样,Go 会从使用的值推断其数据类型。

布尔型

布尔类型仅可能有两个值:truefalse。 你可以使用关键字 bool 声明布尔类型。 Go 不同于其他编程语言,在 Go 中,你不能将布尔类型隐式转换为 01。 你必须显式执行此操作。

因此,你可以按如下方式声明布尔变量:

`var featureFlag bool = true` 

当我们讨论 Go 中的控制流语句时,我们将在后续模块中使用布尔数据类型。 我们还将在以后的模块中使用这些类型。

字符串

最后,让我们看一下编程语言中最常见的数据类型:string。 在 Go 中,关键字 string 用于表示字符串数据类型。 若要初始化字符串变量,你需要在双引号(")中定义值。 单引号(')用于单个字符(以及 runes,正如我们在上一节所述)。

例如,下面的代码演示了声明和初始化字符串变量的两种方法:

`var firstName string = "John"
lastName := "Doe"
println(firstName, lastName)` 

有时,你需要对字符进行转义。 为此,在 Go 中,请在字符之前使用反斜杠 ()。 例如,下面是使用转义字符的最常见示例:

  • n:新行
  • r:回车符
  • t:选项卡
  • ':单引号
  • ":双引号
  • :反斜杠
    使用以下代码片段来测试转义字符:
`fullName := "John Doe t(alias "Foo")n"
println(fullName)` 

你应会看到以下输出(包括新行):

`John Doe        (alias "Foo")` 

默认值

到目前为止,几乎每次声明变量时,都使用值对其进行了初始化。 但与在其他编程语言中不同的是,在 Go 中,如果你不对变量初始化,所有数据类型都有默认值。 此功能非常方便,因为在使用之前,你无需检查变量是否已初始化。

下面列出了我们目前浏览过类型的几个默认值:

  • int 类型的 0(及其所有子类型,如 int64)
  • float32float64 类型的 +0.000000e+000
  • bool 类型的 false
  • string 类型的空值
    运行以下代码片段以确认前面列出的默认值:
`var defaultInt int
var defaultFloat32 float32
var defaultFloat64 float64
var defaultBool bool
var defaultString string
println(defaultInt, defaultBool, defaultFloat32, defaultFloat64, defaultString)` 

你可以使用类似于此代码的代码来确定我们没有浏览到的数据类型默认值。

类型转换

在上一节中,我们确认在 Go 中隐式强制转换不起作用。 接下来,需要显式强制转换。 Go 提供了将一种数据类型转换为另一种数据类型的一些本机方法。 例如,一种方法是对每个类型使用内置函数,如下所示:

`var integer16 int16 = 127
var integer32 int32 = 32767
println(int32(integer16) + integer32)` 

Go 的另一种转换方法是使用 strconv 包。 例如,若要将 string 转换为 int,可以使用以下代码,反之亦然:

`package main

import "strconv"

func main() {
    i, _ := strconv.Atoi("-42")
    s := strconv.Itoa(-42)
    println(i, s)
}` 

运行前面的代码,并确认它运行并打印 -42 两次。

请注意,在前面的代码中,有一个下划线 (_) 用作变量的名称。 在 Go 中,这意味着我们不会使用该变量的值,而是要将其忽略。 否则,程序将不会进行编译,因为我们需要使用声明的所有变量。 我们将返回到本主题,届时你将在后续模块中了解 _ 通常表示的内容。

创建函数

在 Go 中,函数允许你将一组可以从应用程序的其他部分调用的语句组合在一起。 你可以使用函数来组织代码并使其更易于阅读,而不是创建包含许多语句的程序。 更具可读性的代码也更易于维护。

到目前为止,我们一直在调用 println() 函数,并且在 main() 函数中编写代码。 在本节中,我们将探讨如何创建自定义函数。 我们还将介绍一些可用于 Go 函数的其他技巧。

main 函数

与之交互的函数是 main() 函数。 Go 中的所有可执行程序都具有此函数,因为它是程序的起点。 你的程序中只能有一个 main() 函数。 如果创建的是 Go 包,则无需编写 main() 函数。 我们将在后续模块中介绍如何创建包。

在深入了解如何创建自定义函数的基本知识之前,让我们看看 main() 函数的一个重要特性。 你可能留意到,main() 函数没有任何参数,并且不返回任何内容。 但这并不意味着其不能从用户读取值,如命令行参数。 如要访问 Go 中的命令行参数,可以使用用于保存传递到程序的所有参数的 os 包 和 os.Args 变量来执行操作。

下面的代码从命令行读取两个数字,并为其求和:

`package main

import (
    "os"
    "strconv"
)

func main() {
    number1, _ := strconv.Atoi(os.Args[1])
    number2, _ := strconv.Atoi(os.Args[2])
    println("Sum:", number1+number2)
}` 

os.Args 变量包含传递给程序的每个命令行参数。 由于这些值的类型为 string,因此需要将它们转换为 int 以进行求和。

若要运行程序,请使用以下命令:

`go run main.go 3 5` 

输出如下:

`Sum: 8` 

让我们看看如何重构上述代码,并创建第一个自定义函数。

自定义函数

下面是用于创建函数的语法:

`func name(parameters) (results) {
    body-content
}` 

请注意,使用 func 关键字来定义函数,然后为其指定名称。 在命名后,指定函数的参数列表。 你可以指定零个或多个参数。 你还可以定义函数的返回类型,该函数也可以是零个或多个。 (我们将在下一节中讨论如何返回多个值)。在定义所有这些值之后,你可以编写函数的正文内容。

若要练习此技巧,我们将重构上一节的代码,为自定义函数中的数字求和。 我们将使用以下代码:

`package main

import (
    "os"
    "strconv"
)

func main() {
    sum := sum(os.Args[1], os.Args[2])
    println("Sum:", sum)
}

func sum(number1 string, number2 string) int {
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    return int1 + int2
}` 

此代码创建一个名为 sum 的函数,该函数采用两个 string 参数,并将它们强制转换为 int,然后返回求和所得的结果。 定义返回类型时,函数需要返回该类型的值。

在 Go 中,你还可以为函数的返回值设置名称,将其当作一个变量。 例如,你可以重构如下 sum 函数:

`func sum(number1 string, number2 string) (result int) {
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    result = int1 + int2
    return
}` 

请注意,你现在需要将函数的结果值括在括号中。 你还可以在函数中使用该变量,并且只需在末尾添加 return 行。 Go 将返回这些返回变量的当前值。 在函数末尾编写 return 关键字非常简单方便,尤其是在有多个返回值时。 但我们不建议采用这种方法,因为它不太清楚函数返回的内容。

返回多个值

在 Go 中,函数可以返回多个值。 你可以采用类似于定义函数参数的方式来定义这些值。 换句话说,你可以指定一个类型和名称,但该名称是可选的。

例如,假设你想要创建一个函数,以将两个数字求和,又让它们相乘。 函数代码将如下所示:

`func calc(number1 string, number2 string) (sum int, mul int) {
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    sum = int1 + int2
    mul = int1 * int2
    return
}` 

你现在需要两个变量来存储函数的结果。 (否则就不会进行编译。)它的外观如下所示:

`func main() {
    sum, mul := calc(os.Args[1], os.Args[2])
    println("Sum:", sum)
    println("Mul:", mul)
}` 

Go 的另一个有趣功能是,如果不需要函数的某个返回值,可以通过将返回值分配给 _ 变量来放弃该函数。 _ 变量是 Go 忽略返回值的惯用方式。 它允许程序进行编译。 因此,如果只需要求和,则可以使用以下代码:

`func main() {
    sum, _ := calc(os.Args[1], os.Args[2])
    println("Sum:", sum)
}` 

在后续模块中探讨错误处理时,我们将详细介绍如何忽略函数的返回值。

更改函数参数值(指针)

将值传递给函数时,该函数中的每个更改都不会影响调用方。 Go 是“按值传递”编程语言。 这意味着每次向函数传递值时,Go 都会使用该值并创建本地副本(内存中的新变量)。 在函数中对该变量所做的更改都不会影响你向函数发送的更改。

例如,假设你创建了一个用于更新人员姓名的函数。 请注意,运行此代码时会发生的变化:

`package main

func main() {
    firstName := "John"
    updateName(firstName)
    println(firstName)
}

func updateName(name string) {
    name = "David"
}` 

即使你在函数中将该名称更改为 David,输出仍为 John。 由于 updateName 函数中的更改仅会修改本地副本,因此输出不会发生变化。 Go 传递变量的值,而不是变量本身。

如果你希望在 updateName 函数中进行的更改会影响 main 函数中的 firstName 变量,则需要使用指针。 指针 是包含另一个变量的内存地址的变量。 当你发送指向某个函数的指针时,不会传递值,而是传递地址内存。 因此,对该变量所做的每个更改都会影响调用方。

在 Go 中,有两个运算符可用于处理指针:

  • & 运算符使用其后对象的地址。
    • 运算符取消引用指针。 也就是说,你可以前往指针中包含的地址访问其中的对象。

让我们修改前面的示例,以阐明指针的工作方式:

`package main

func main() {
    firstName := "John"
    updateName(&firstName)
    println(firstName)
}

func updateName(name *string) {
    *name = "David"
}` 

运行前面的代码。 请注意,输出现在显示的是 David,而不是 John

首先要做的就是修改函数的签名,以指明你要接收指针。 为此,请将参数类型从 string 更改为 *string。 (后者仍是字符串,但现在它是指向字符串 的 指针。)然后,将新值分配给该变量时,需要在该变量的左侧添加星号 (*) 以暂停该变量的值。 调用 updateName 函数时,系统不会发送值,而是发送变量的内存地址。 这就是前面的代码在变量左侧带有 & 符号的原因。

了解包

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 不会提供 publicprivate 关键字,以指示是否可以从包的内外部调用变量或函数。 但 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 博客](https://blog.golang.org/using-go-modules)中发布的系列帖子。

引用本地包(模块)

现在,让我们先使用包。 我们将继续使用我们一直使用的示例应用程序。 这一次,我们将使用之前在 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 关键字指定使用本地目录,而不是模块的远程位置。 在这种情况下,由于 helloworldcalculator 程序在 $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 将下载其所有依赖项。

总结

我们介绍了开始在 Go 中构建更复杂应用程序所需的基础知识。 现在,你可以通过几种方法来声明和初始化变量。 你还了解 Go 提供的各种数据类型。 你已经使用最基本的数据类型,

并且还了解到如何创建函数来组织代码,并使代码更易于维护。 你已经了解到,Go 是“按值传递”语言,但它亦支持指针。 我们将在后续的部分模块中使用指针。

最后,你已经了解包在 Go 中的工作原理,以及如何在需要与其他开发人员共享代码时创建模块。 你已经了解到,如果只需要创建独立的应用程序,则所有代码都需要成为 main 包的一部分。 你已经了解到,该程序的起点是 main() 函数。 现在,你已了解如何引用本地和第三方模块。 构建 Go 程序时,你将会使用这些模块。

本文转自:SDK社区(sdk.cn)是一个中立的社区,这里有多样的前端知识,有丰富的api,有爱学习的人工智能开发者,有风趣幽默的开发者带你学python,还有未来火热的鸿蒙,当各种元素组合在一起,让我们一起脑洞大开共同打造专业、好玩、有价值的开发者社区,帮助开发者实现自我价值!


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:聚合数据

查看原文:了解Go中如何使用包、变量和函数

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

850 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传