关于 go 语法的一些碎碎念
Table of Contents
go 语言变量声明
go 语言与 c 语言类似,以面向数据的设计为主,通过一个个的函数组织代码,大多数的语法也和 c 相似。但是,go 与 c 的对比也有较多的不同。其中最明显的一点就是 go 语言声明变量将变量的类型放在了变量名称的后面。尤其在函数的使用中,两者的表达的清晰程度明显有所区别。下面给出了相关代码的对比,在对应的代码块中,前者为 c 语言,后者为 go 语言。
-
基本语法的对比:
int x; int *p; int a[3]; int f(int, int);
var x int var p *int var a [3]int func f(int, int) int
在简单的变量、函数声明面前,二者并不能体现出明显差别。
-
函数指针的对比
int (*fp)(int, int); // 去除函数名的形式 // int (*)(int, int); // 容易与下面的函数声明混淆 // int *(int, int);
var fp func(int, int) int // 去除函数名的形式 // var func(int, int) int
可以看到在 c 语言中,由于函数指针需要与函数区分开,所以引入了
*
作为指针来区分两者。由于指针具有多种含义,可以是函数指针,也可以是变量指针,且在 c 中函数指针与函数返回值紧紧靠在一起。函数指针的*
容易与返回值混淆造成混乱。而 go 语言恰好对 c 语言的声明作了相应的改进,淡化了指针的概念,使用
func
代表函数指针,且进行了函数指针和返回值的分离。因此,go 与 c 语言相比,看起来更加清晰。 -
对于更复杂的情况:如函数指针的指向的函数的参数包含函数指针,函数指针指向的函数的返回值是函数指针等,还有其它更加复杂的情况。
int (*(*fp)(int (*)(int, int), int))(int, int);
var fp func(int, int) func(func(int, int) int, int) int
上面两个代码块的函数指针声明等效,都指向一个参数列表为两个 int、返回值为函数指针的函数。该函数指针(返回值)指向的是返回值是 int,参数列表为函数指针和 int 的函数。参数列表的函数指针指向的函数为返回值为 int、参数列表为两个 int 的函数。
面对上述如此复杂的函数指针声明,go 语言显然处理得比 c 语言合适。c 语言中由于返回值是一个函数指针,出现了令人难以接受的
(*(*fp))
表示方式。而 go 语言的声明显然层次性更好。
go 语言选择将类型名放在变量名之后,解决了 c 语言处理函数指针声明所遇到的难题。将函数指针声明的各个部分划分清楚。
而且,在阅读 go 源代码的时候更符合我们的习惯,能够第一眼看到变量的名称。先阅读变量名,能让我们快速把握代码的意思,而不必纠结于变量的类型等其它信息,更符合自顶向下的思想。
下划线的使用
go 语言的代码中,有时候会用到下划线 _
作为变量名。例如,官方的 go 安装教程中有这样的一段测试代码:
package stringutil
import "testing"
func TestReverse(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := Reverse(c.in)
if got != c.want {
t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
}
}
}
此时,range
的第一个返回值是数据的索引,第二个返回值是数据的值,即为对应的 case
。显然,在测试中我们只关心 cases
中的每一个样例 case
,对索引我们不感兴趣。因此,我们使用了占位符 _
,忽略这个变量。同时,使用 _
还可以告诉编译器,让其进行相应的优化,提高代码运行速度。另外,go 语言的编译器不允许代码中出现未使用的变量。如果不使用占位符 _
,出现了不使用的变量名,编译器会报错无法成功编译。
补充:有时候在 import
语句中,使用了下划线表示不将完整的包导入到代码中,而仅仅是调用了 init()
函数,此时无法调用包中的其它函数。