8.2. 语法

Go中变量的声明语法和C++相反。定义变量时,先写变量的名字,然后是变量的类型。这样 不会出现像C++中, 类型不能匹配后面所有变量的情况(指针类型)。而且语法清晰,便于 阅读。

  Go                           C++
  var v1 int                // int v1;
  var v2 string             // const std::string v2;  (approximately 近似等价)
  var v3 [10]int            // int v3[10];
  var v4 []int              // int* v4;  (approximately 近似等价)
  var v5 struct { f int }   // struct { int f; } v5;
  var v6 *int               // int* v6;  (but no pointer arithmetic 没有指针算术)
  var v7 map[string]int     // unordered_map<string, int>* v7;  (approximately 近似等价)
  var v8 func(a int) int    // int (*v8)(int a);

变量的声明通常是从某些关键字开始,例如var, func,const或type。对于类型的专有方法定义, 在变量名前面还要加上对应该方法发类型对象变量,细节清参考discussion of interfaces。

你也可以在关键字后面加括号,这样可以同时定义多个变量。

  var (
      i int
      m float
  )

When declaring a function, you must either provide a name for each parameter or not provide a name for any parameter; you can't omit some names and provide others. You may group several names with the same type:

定义函数的时候,你可以指定每个参数的名字或者不指定任何参数名字,但是你不能只指定部分函数参数的 名字。如果是相邻的参数是相同的类型,也可以统一指定类型。

  func f(i, j, k int, s, t string)

对于变量,可以在定时进行初始化。对于这种情况,我们可以省略变量的类型部分,因为Go编译器 可以根据初始化的值推导出变量的类型。

  var v = *p

如果变量定义时没有初始化,则必须指定类型。没有显式初始化的变量,会被自动初始化为空的值, 例如0,nil等。Go不存在完全未初始化的变量。

用:=操作符,还有更简短的定义语法:

  v1 := v2

和下面语句等价:

  var v1 = v2

Go还提供多个变量同时赋值:

  i, j = j, i    // Swap i and j.

函数也可以返回多个值,多个返回值需要用括号括起来。返回值可以用一个等于符号赋给 多个变量。

  func f() (i int, j int) { ... }
  v1, v2 = f()

Go中使用很少的分号,虽然每个语句之间实际上是用分号分割的。因为,go编译器会在看似 完整的语句末尾自动添加分号(具体细节清参考Go语言手册)。 当然,自动添加分号也可能带来一些问题。例如:

  func g()
  {                  // INVALID
  }

在g()函数后面会被自动添加分号,导致函数编译出错。下面的代码也有类似的 问题:

  if x {
  }
  else {             // INVALID
  }

在第一个花括号}的后面会被自动添加分号,导致else语句 出现语法错误。

分号可以用来分割语句,你仍然可以安装C++的方式来使用分号。不过Go语言中,常常省略不 必要的分号。 只有在 循环语句的初始化部分,或者一行写多个语句的时候才是必须的。

继续前面的问题。我们并不用担心因为花括号的位置导致的编译错误,因此我们可以用 gofmt 来排版程序代码。 gofmt 工具总是可以将代码排版成统一的风格。While the style may initially seem odd, it is as good as any other style, and familiarity will lead to comfort.

当用指针访问结构体的时候,我们用.代替->语法。 因此,用结构体类型和结构体指针类型访问结构体成员的语法是一样的。

  type myStruct struct { i int }
  var v9 myStruct              // v9 has structure type
  var p9 *myStruct             // p9 is a pointer to a structure
  f(v9.i, p9.i)

Go不要求在if语句的条件部分用小括弧,但是要求if后面的代码 部分必须有花括弧。类似的规则也适用于for和switch等语句。

  if a < b { f() }             // Valid
  if (a < b) { f() }           // Valid (condition is a parenthesized expression)
  if (a < b) f()               // INVALID
  for i = 0; i < 10; i++ {}    // Valid
  for (i = 0; i < 10; i++) {}  // INVALID

Go语言中没有while和do/while循环语句。我们可以用只有一个 条件语句的for来代替while循环。如果省略for 的条件部分,则是一个无限循环。

Go增加了带标号的break 和continue语法。不过标号必须 是针对for,switch或select代码段的。

对于switch语句,case匹配后不会再继续匹配后续的部分。 对于没有任何匹配的情况,可以用fallthrough 语句。

  switch i {
  case 0:  // empty case body
  case 1:
      f()  // f is not called when i == 0!
  }

case语句还可以带多个值:

  switch i {
  case 0, 1:
      f()  // f is called if i == 0 || i == 1.
  }

case语句不一定必须是整数或整数常量。如果省略switch的 要匹配的值,那么case可以是任意的条件语言。

  switch {
  case i < 0:
      f1()
  case i == 0:
      f2()
  case i > 0:
      f3()
  }

++ 和 -- 不再是表达式,它们只能在语句中使用。因此, c = p++ 是错误的。语句 p++ 的含义也完全不同,在go中等价于 (*p)++ 。

defer可以用于指定函数返回前要执行的语句。

  fd := open("filename")
  defer close(fd)         // fd will be closed when this function returns.