现在你已经对Go语言有了初步了解并完成了第一个练习,我们将深入探讨变量。我们的第一个重点是变量的声明。
变量用来暂时存储数据,以便你可以对其进行操作。当你声明一个变量时,你需要四个要素:声明变量的语句、变量的名称、变量能够存储的数据类型以及它的初始值。幸运的是,其中一些部分是可选的,这也意味着有多种方式来定义变量。
让我们详细了解所有声明变量的方法。
使用 var 是声明变量的基础方式。我们将要介绍的其他方法通常是这种方式的变体,通常通过省略一些部分来实现。一个完整的 var 声明如下:
var foo string = "bar"
其中关键部分包括 var、foo、string 和 = “bar”:
在这个练习中,我们将使用完整的 var 语法声明两个变量,并将它们打印到控制台。你将看到 var 语法可以在代码的任何地方使用,这并不是所有变量声明语法都能做到的。开始吧:
package main
import (
"fmt"
)
var foo string = "bar"
func main() {
var baz string = "qux"
fmt.Println(foo, baz)
}
go run .
预期输出结果如下:
bar qux
在这个例子中,foo 在包级别声明,而 baz 在函数级别声明。变量声明的位置很重要,因为声明变量的位置也限制了你可以使用的声明语法。
接下来,我们将看另一种使用 var 语法的方式。
我们可以使用一个 var 声明来定义多个变量,这可以通过 var 块或语句来实现。这种方法在声明包级变量时非常常见。变量不需要是相同的类型,并且它们可以有各自的初始值。语法如下:
var (
<name1> <type1> = <value1>
<name2> <type2> = <value2>
…
<nameN> <typeN> = <valueN>
)
你可以有多个这种类型的声明。这是一种很好的组织相关变量的方式,使你的代码更具可读性。尽管你可以在函数中使用这种语法,但在函数中看到这种用法的情况比较少见。
在这个练习中,我们将使用一个 var 语句声明多个变量,每个变量有不同的类型和初始值。然后,我们将打印每个变量的值到控制台。开始吧:
package main
import (
"fmt"
"time"
)
var (
Debug bool = false
LogLevel string = "info"
startUpTime time.Time = time.Now()
)
func main() {
fmt.Println(Debug, LogLevel, startUpTime)
}
go run .
在这个练习中,我们使用一个 var 语句声明了三个变量。你的输出结果可能会有所不同,特别是 time.Time 变量的值会随着时间而不同,但格式是相同的。
像这样使用 var 语法是一种很好的方法来保持代码的良好组织,同时也减少了输入量。
接下来,我们将开始去掉 var 语法中的一些可选部分。
在实际编码中,使用完整的 var 语法并不常见。通常只有在你需要定义一个具有初始值并且需要严格控制其类型的包级变量时,才会使用完整的语法。到那时,你会明显感受到类型不匹配的问题,所以现在不必太担心。其他时候,你可以省略一些可选部分,或者使用简短的变量声明方式。
在声明变量时,你不必同时包含类型和初始值。你可以只使用其中之一;Go 会处理剩下的部分。如果声明中有类型但没有初始值,Go 会使用该类型的零值。关于零值,我们将在后面章节详细讨论。另一方面,如果有初始值而没有类型,Go 会根据你使用的字面量值来推断所需的类型。
在这个练习中,我们将更新之前的练习,使其跳过变量声明中的可选初始值或类型声明。然后,我们将像之前一样将这些值打印到控制台,以展示结果是一致的。开始吧:
package main
import (
"fmt"
"time"
)
var (
Debug bool
LogLevel = "info"
startUpTime = time.Now()
)
func main() {
fmt.Println(Debug, LogLevel, startUpTime)
}
go run .
输出显示即使在声明变量时没有提及类型,变量的值仍然正确
在这个练习中,我们能够更新之前的代码,使用更紧凑的变量声明方式。声明变量是你在编程中经常需要做的事情,省略这些冗余部分能让你的编码体验更好。
接下来,我们将探讨一种不能跳过任何部分的情况。
有时你需要使用声明的所有部分——例如,当 Go 无法推断出你需要的正确类型时。以下是一个示例:
package main
import "math/rand"
func main() {
var seed = 1234456789
rand.NewSource(seed)
}
执行以上代码会出现错误。
问题在于 rand.NewSource 需要一个 int64 类型的变量。Go 的类型推断规则会将像我们使用的数字解释为 int 类型。后续章节中,我们会详细讨论 int 和 int64 的区别。为了解决这个问题,我们需要在声明中明确指定 int64 类型,如下所示:
package main
import "math/rand"
func main() {
var seed int64 = 1234456789
rand.NewSource(seed)
}
接下来,我们将介绍一种更快捷的变量声明方式。
在函数内部声明变量时,我们可以使用 := 简写。这种简写方式允许我们使声明更加简洁。它通过省略 var 关键字,并总是从需要的初始值中推断出类型来实现这一点。
在这个练习中,我们将更新之前的代码,使用短变量声明的方式。由于短变量声明只能在函数内使用,我们会将变量从包级别作用域移动到函数内。之前 Debug 变量有类型但没有初始值,现在我们将它更改为具有初始值,因为在使用短变量声明时这是必需的。最后,我们将打印这些变量到控制台。开始吧:
package main
import (
"fmt"
"time"
)
func main() {
Debug := false
LogLevel := "info"
startUpTime := time.Now()
fmt.Println(Debug, LogLevel, startUpTime)
}
go run .
在这个练习中,我们将之前的代码更新为使用非常简洁的变量声明方式,只要有初始值即可。
:= 简写在 Go 开发者中非常受欢迎,它是现实世界 Go 代码中最常见的变量定义方式。开发者喜欢它使代码简洁紧凑,同时仍然清晰明了。
另一个快捷方式是将多个变量声明在同一行。
你可以使用短变量声明同时声明多个变量。它们必须都在同一行,并且每个变量都必须有对应的初始值。其语法形式为 , , …, := , , …, 。变量名位于 := 左侧,并用 , 分隔。初始值位于 := 右侧,也用 , 分隔。左侧的第一个变量名对应右侧的第一个值。变量名和初始值的数量必须相等。
以下是一个使用之前练习代码的示例:
package main
import (
"fmt"
"time"
)
func main() {
Debug, LogLevel, startUpTime := false, "info", time.Now()
fmt.Println(Debug, LogLevel, startUpTime)
}
在实际代码中,有时会看到这样的写法。虽然这种写法有点难以阅读,但它在处理函数返回多个值时非常常见。我们将在后续章节中详细讨论这一点。
在这个练习中,我们将调用一个返回多个值的函数,并将每个值赋给一个新变量。然后,我们将这些变量打印到控制台。开始吧:
package main
import (
"fmt"
"time"
)
func getConfig() (bool, string, time.Time) {
return false, "info", time.Now()
}
func main() {
Debug, LogLevel, startUpTime := getConfig()
fmt.Println(Debug, LogLevel, startUpTime)
}
go run .
在这个练习中,我们调用了一个返回多个值的函数,并使用短变量声明在一行中捕获了这些值。如果使用 var 语法,它将如下所示:
var (
Debug bool
LogLevel string
startUpTime time.Time
)
Debug, LogLevel, startUpTime = getConfig()
短变量声明是一种使 Go 看起来像动态语言的关键特性。
不过,var 语法还有一个有用的技巧,我们将在接下来的内容中继续探索。
虽然短变量声明更常用,但你也可以使用 var 在一行中定义多个变量。需要注意的是,当声明类型时,所有的值必须具有相同的类型。如果你使用初始值,那么每个值可以根据字面量值推断其类型,因此可以有不同的类型。以下是一个示例:
package main
import (
"fmt"
"time"
)
func getConfig() (bool, string, time.Time) {
return false, "info", time.Now()
}
func main() {
// 仅声明类型
var start, middle, end float32
fmt.Println(start, middle, end)
// 初始值,混合类型
var name, left, right, top, bottom = "one", 1, 1.5, 2, 2.5
fmt.Println(name, left, right, top, bottom)
// 也适用于函数
var Debug, LogLevel, startUpTime = getConfig()
fmt.Println(Debug, LogLevel, startUpTime)
}
在实际应用中,短变量声明通常会更简洁,因此 var 语法不常用于多个变量的声明。唯一的例外是当你需要声明多个相同类型的变量时,这种语法可以帮助你更好地控制类型。
Go 是一个支持 UTF-8 的语言,这意味着你可以使用除拉丁字母外的其他字母来定义变量名。例如,以下代码展示了如何使用不同语言的字母作为变量名:
package main
import (
"fmt"
"time"
)
func main() {
デバッグ := false
日志级别 := "info"
ይጀምሩ := time.Now()
_A1_Μείγμα := ""
fmt.Println(デバッグ, 日志级别, ይጀምሩ, _A1_Μείγμα)
}
虽然可以这样定义,但是不建议。
现在我们已经定义了变量,接下来让我们看看如何修改变量的值。修改变量的值与设置初始值的语法类似,使用 = 这样的格式。
请按照以下步骤操作:
package main
import "fmt"
func main() {
offset := 5
fmt.Println(offset)
offset = 10
fmt.Println(offset)
}
go run .
输出结果如下:
5
10
在这个例子中,我们将 offset 的值从初始值 5 修改为 10。任何使用原始值的地方,例如我们示例中的 5 和 10,你都可以使用变量。以下是如何使用变量替代原始值的示例:
package main
import "fmt"
var defaultOffset = 10
func main() {
offset := defaultOffset
fmt.Println(offset)
offset = offset + defaultOffset
fmt.Println(offset)
}
输出结果如下:
10
20
在这个例子中,我们将 offset 的初始值设为 defaultOffset,然后将 offset 更新为 offset + defaultOffset。这种方法使得我们可以在代码中使用变量来替代硬编码的值,提高了代码的灵活性和可维护性。
接下来,我们将看看如何在一行中修改多个变量的值。
与在一行中声明多个变量类似,你也可以在一行中同时修改多个变量的值。其语法也非常类似,格式为 , , …, = , , …, 。
在本练习中,我们将定义一些变量,并使用一行语句来修改它们的值。然后,我们会打印出这些变量的新值。让我们开始吧:
package main
import "fmt"
func main() {
query, limit, offset := "bat", 10, 0
query, limit, offset = "ball", offset, 20
fmt.Println(query, limit, offset)
}
go run .
输出结果如下,显示了一行语句中修改后的变量值:
ball 0 20
在本练习中,我们成功地在一行中修改了多个变量的值。这种方法也可以用于函数调用,就像变量声明一样。不过,你需要小心使用这种特性,确保代码首先易于阅读和理解。如果使用一行语句使得代码难以理解,那么分多行写代码可能会更清晰。
接下来,我们将探讨操作符是什么,以及它们如何被用来以有趣的方式改变你的变量。