一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情
前言
该系列Go使用的版本是:
go version go1.16.5 darwin/amd64
看完本篇,我们将学习到
- 使用Go写一个Hello World程序
- 了解Go程序源代码基本元素
- Go代码风格规则
第一个Go程序
创建main.go文件,然后复制下面代码到文件中
package main
import "fmt"
func main() {
fmt.Println("Hello world")
}
代码解读
- 每个源代码的开头都是一个package声明,表明所属的包。
- 接下来是一系列的import语句, 用于导入该程序所依赖的包。
- 接下来是main函数, 这是一个特殊的函数,和大多数语言一样,该函数是程序的执行入口。 在Go中,main函数是不能带有参数以及返回值。
- 使用fmt包中Println函数向控制台输出Hello world
运行代码
- 通过
go run命令运行,该命令会将编译编译,链接以及运行三个步骤合并成一步,运行后不会在目录中看到任何中间文件以及最终的可执行文件:
$ go run .
Hello world
- 通过
go build命令, 该命令将编译链接生成可执行文件,然后在手动运行可执行文件
$ go build main.go
$ ./main
Hello world
一个相对复杂的程序
为了Go源代码的基本元素有个大致的了解,让我们来看一个相对复杂的代码,创建一个basic_code_element_demo.go文件,并复制下面代码
package main // 声明当前源文件所属包
import (
"fmt"
"math/rand"
) // 引入一个标准库包
const GenerateRandomNumbers = 20 // 声明一个有名的整型常量,表示生成随机数的个数
/*
StatRandomNumbers根据传入的参数maxRand
生成GenRandNum个小于maxRand的非负随机整数,并统计返回大于和小于等于maxRand的随机个数
*/
func StatRandomNumbers(maxRand int) (int, int) {
// 声明两个整型变量,初始值为0
var a, b int
// for循环代码块
for i := 0; i < GenerateRandomNumbers; i++ {
// if-else条件代码块
if rand.Intn(maxRand) < maxRand/2 {
a = a + 1
} else {
b++ // 自增, 等价于 b = b + 1
}
}
return a, b // 返回结果
}
// 程序入口, 主函数, 不能有参数和返回值
func main() {
var maxRand = 16 // 定义个整型变量,并初始化为16
gt, le := StatRandomNumbers(maxRand) // 定义了两个整型变量, 并使用函数返回值进行初始化
fmt.Println("Result:", gt, ",", le)
}
运行程序
$ go run basic_code_element_demo.go
Result: 11 , 9
从上面程序, 可以了解到部分的关键词, 变量常量的声明和使用, 循环和条件流程控制语句,函数定义等基本元素。如果有小伙伴通过自行输入该代码, 并根据自己的代码风格进行改写, 也许就会发现,在运行的时候报错了。 比如, 有些小伙伴对于做大括号{是另起一行,比如我们将StatRandomNumbers改写成
func StatRandomNumbers(maxRand int) (int, int)
{ // 编译错误 syntax error: unexpected semicolon or newline before {
var a, b int
for i := 0; i < GenerateRandomNumbers; i++
{ // 编译错误 syntax error: unexpected newline
if rand.Intn(maxRand) < maxRand/2
{ // 编译错误 syntax error: unexpected newline
a = a + 1
} else {
b++
}
}
return a, b
}
为什么会出现编译错误呢?这是因为Go语法是使用分号;作为结尾标识符的,只是大多数情况分号;是可以省略的,而在编译阶段,编译器在自动加入这些省略的分号;。 使得在代码风格上有一定的限制, 这样限制的好处是
- 可以让程序编译的更快
- 可以让大家的风格保持统一,在不同程序员所写代码之间更加具有可读性, 也避免了在风格上有分歧而争论不休
Go的代码风格
分号插入规则
那么Go在风格限制有怎样的规则可循呢? 这个需要知道;何时会被自动插入,在Go的语言规范 是这么描述的
- 讲输入分解成一系列的单词符号,并且一行的最后一个单词符号(token)是下列情况时,
;将自动插入到该单词符号之后- 标识符
- 整型、浮点数、虚数、码点(utf-8字符)或者字符串字面值
return、break、continue、fallthrough等关键词其中一个++、--、}、]、)运算符或者括号符号等其中一个
- 允许复杂的语句写成一条语句, 在
)或者}之前可能插入;根据第一条规则, 下面的代码我们可以省略;
b := true // 标识符结尾
var a = 1 // 字面值结尾
func f() {
return // 跳转关键词
}
f() // 括号符号
并且无法使用下面链式调用
obj // 编译错误 因为会在这后面插入分号
.methodA() // 同上
.methodB() // 同上
.methodC() // 同上
根据第二条规则, 下面代码是合法, 但是语句与语句之间的分号还是不可省略
import (_ "math"; "fmt")
var (a int; b int)
func f() {fmt.Println("hello"); fmt.Println("world")}
var a = 1; var b = 2
个人觉得在Go 101 中关于自增和自减的解释有点牵强, 如下面代码, 它给出的解释是在)之前++之后插入;而导致语法错误。
func f() {
a := 0
println(a++) // 因为等价于 println(a++;) unexpected ++, expecting comma or )
println(a--) // 因为等价于 println(a--;)
}
如果按此说法,那么下面代码应该是正确的才对。
func f() {
a := 0
b := a++ // 这句如果按照上面来解释, 应该正确才是, 实际也会提示语法错误
}
所以对于自增++和自减--语句来说, 它本身有语法限制,只能是单独的语句, 不能作为表达式出现。
逗号,从不会被自动插入
因为编译器在任何情况下, 都不会自动插入逗号,,因此在必要时, 我们需要手动插入,,以防止分号;插入。下面代码是合法
func f(a int, b int) (bool, int, /* 合法, 但可以省略 */) {
return true, 1
}
var _ = []int{2, 3, 5, 7, 9, /* 合法, 但可以省略 */}
var _ = []int{2, 3, 5, 7, 9, /* 不可省略, 否则编译错误 */
}
而下面代码是不合法
func f(a int, b int) (bool, int // 编译错误,可以手动插入逗号来防止分号的插入
) {
return true, 1
}
var _ = []int{2, 3, 5, 7, 9 // 编译错误,可以手动插入逗号来防止分号的插入
}
在编写代码时, 我们可以使用go fmt 以及go vet命令来格式化代码和发现可能存在的逻辑错误。