Go 语言基础语法和常用特性解析 | 青训营

100 阅读4分钟

1. 前言

这篇文章可以帮助你快速地了解 Go 语言中的一些语法和其有别于其他语言的特点。

2. 基础语法

Go 语言是由 C 语言改进而来的,因此语法贴近 C 系语言。在 Go 中,我们可以用以下语法来定义变量:

var name string = "World"

var 用于引出变量定义语法,string 是 Go 内置的字符串类型,"World" 是变量 name 的初始值。其中类型可以省去,Go 会自动根据初始值推导;初始值也可省去,Go 会使用空值去初始化变量(但两者不可同时省去)。这里可以发现 Go 有别于 C 的一个特征:类型后置。在 C 中,我们会这么定义一个数组:int arr[4]; 而 Go 的写法则是 var arr [4]int,这样做的一大好处就是我们不再需要使用螺旋法则去理解复杂的类型定义,而是可以直接从左向右读。在函数体中,Go 还提供了一种定义变量的语法:

name := "World"

其与普通的赋值几乎没有任何差别,除了使用的符号是 := 而不是 =。你还可以同时赋值/定义多个变量:

x, y := 1, 2
x, y = y, x

值得注意的是,Go 的语句是需要以 ; 结尾的,而上面的代码都不包括 ;,这其实是 Go 有意为之的代码风格,Go 会在满足条件的 newline 前自动添加一个分号。这同时也意味着你无法写出某些风格的代码,如:

// 正确写法
if x > 0 {
    // ...
}
// 错误写法!
if x > 0
{
    // ...
}

下面来介绍流程控制语句。if 与 C 的语法几乎一致,只不过 Go 允许你略去条件外的括号:

if x > 0 {
    a()
} else {
    b()
}

Go 只有 for 循环,但涵盖了 C 中的大多数循环语法:

// 死循环
for {
    break
}
// 三段式循环(与 C 的 for 完全一致,可省略三段中的任意部分)
for i := 0; i < n; i++ {
    f()
}
// 单条件循环
for i < n {
    i++
}
// 范围循环(有 range 关键字,i、v 分别为索引、值)
// 如果不需要 i 或 v 可以用 _ 占位符代替
for i, v := range [4]int{1, 2, 3, 4} {
    f()
}

Go 支持比 C 更强大的 switch,可以匹配字符串、变量标签值或类型,且无需添加 break

candidate := "str"
switch "str2" {
case "s":
    a()
case candidate:
    b()
default:
    c()
}
// switch 变量名.(type),其中变量必须为 interface 类型
var x interface{} = 42
switch x.(type) {
case int:
    a()
default:
    b()
}

Go 的函数定义语法为:

func 函数名(参数名 参数类型) 返回类型 {
    // ...
}

Go 的函数可选添加接收者、泛型或者返回值的名称,这些语法将在后面一一介绍。Go 的全局函数、类型定义等可以无视其在文件中的定义顺序来使用,这与 C 相比是很大的进步。如果想在 Go 内使用其他人定义的库(包括标准库)的话可以使用 import "名称" 语法。下面来展示一个 Hello world 程序:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world!")
}

其中 package main 是固定语法,它用于指示编译器在哪里寻找入口点函数(即 main);fmt.Println 为输入输出库中的打印行函数,它可以接收任意个数、任意类型的参数,并通过空格分隔将它们输出为字符串,最后附加一个换行。fmt.Printf 是另一个输出函数,它的工作方式类似 C 中的 printf,不过其支持一些 C 无法支持的用法:

x := 42
fmt.Println("(%T, %v)\n", x, x)

这段代码会输出 (int, 42),能做到这点得益于 Go 的反射能力,这意味着我们不再需要手动维护格式字符串与参数类型间的联系,无形中了节省了许多查 BUG 的时间。类似的函数还有 fmt.Sprintf,其类似 C 中的 sprintf,会返回一个字符串而不是将结果输出到屏幕上。在上面调用函数的代码中,我们可以发现所有的函数都是首字母大写,这并非巧合,而是 Go 的强制要求:如果库的作者希望一个名称对外部的使用者可见,它们的名称就必须做到首字母大写,否则它们会被认为是仅供库内部使用的私有函数。

未完待续……