# 语句

语句(statement)执行一到多个动作,表达式(expression)计算并返回结果。 表达式属于语句,而语句未见得是表达式。

关键字
仅 25 个 保留关键字(keywords),体现语法规则的简洁性。 保留关键字不能用作常量、变量、函数名以及结构字段等标识符。

break        default       func        interface   select
case         defer         go          map         struct
chan         else          goto        package     switch
const        fallthrough   if          range       type
continue     for           import      return      var

符号
符号列表。其中,自增( ++ )、自减( -- )是语句,且没有三元运算符( ?: )。 ^ 即是一元 NOT,也是二元 XOR。而 ~ 为泛型新增。

+      &       +=      &=       &&      ==      !=      (      )
-      |       -=      |=       ||      <       <=      [      ]
*      ^       *=      ^=       <-      >       >=      {      }
/      <<      /=      <<=      ++      =       :=      ,      ;
%      >>      %=      >>=      --      !       ...     .      :
       &^              &^=              ~

二元
一元优先级最高,二元分五个级别。相同优先级的二元运算符,从左往右依次计算。

highest 	 *     /     %     <<    >>    &    &^
 	         +     -     |     ^ 
 	         ==    !=    <     <=    >     >=
 	         &&
lowest	   ||

除位移外,二元操作数类型必须相同。

func main() {
    var a int
    var b int64
    // _ = a + b // ~ invalid operation: mismatched types int and int64
}

位移右操作数是整数类型,或可转换的无类型常量。

func main() {
	b := 23
	x := 1 << b  
	println(x)
}

位清除(AND NOT, bit clear)和 异或(XOR)不同。
清除将左右操作数对应二进制位都为 1 的重置为 0(类似位图),以达到一次清除多个标记位的目的。

0101 & 0011 = 0001  // AND   与:都为 1
0101 | 0011 = 0111  // OR    或:至少一个 1
0101 ^ 0011 = 0110  // XOR   异或:只有一个 1
      ^0111 = 1000  // NOT   取反 (一元)
func main() {
	a := 0b01100101
	b := 0b11010100
	
	x := a ^ b   // 10110001
	c := a &^ b  // 00100001
    
	fmt.Printf("%08b\n", x)
	fmt.Printf("%08b\n", c)
}
const (
	read    byte = 1 << iota
	write
	exec
	freeze
)
func main() {
	a := read | write | freeze
	b := read | exec
    
	c := a &^ b 
    
	println(c == write | freeze) // true
	println(c & write == write)  // true
	println(c & read == read)    // fasle
}

初始化

对复合类型(数组、切片、字典、结构体)变量初始化时,有一些语法限制。

初始化表达式须含类型标签。(数组、字典元素可省略)
左大括号不能另起一行。
多个成员初始值以逗号分隔。
允许多行,但每行须以逗号或右大括号结束。

作用域

通常意义上的作用域由大括号构成。作用域构成独立空间,内部所定义成员无法被外部访问。 当然,也有更大范围的作用域,如包(package),它有自己的规则。

作用域限定变量可被谁访问,而生命周期表示对象存活时间(可被不同作用域引用),不同混同。

同级作用域相互隔离。
下层可访问上层作用域成员。
下层作用域遮蔽上层作用域同名成员。

func main() {
	x := 1
	{
		x := "abc"     // 新变量,遮蔽。
		y := 2
		println(x, y)  // abc, 2
	}
	println(x)         // 1
	println(y)         // undefined: y
}

# 选择语句

两种分支选择控制语句。

if:适合逻辑控制,分支数量不宜过多。
switch:适合数据匹配,分支内容不宜过长。

# If

条件表达式值必须是布尔类型,可省略括号,且左大括号不能另起一行。

善用初始化语句,组合调用和返回值处理。
如定义局部变量,其作用域覆盖所有分支。
减少嵌套和分支,让正常逻辑处于相同层次。
如条件表达式过于冗长,可将其重构为函数。

func do(x int) error {
	if x <= 0 {
		return errors.New("x <= 0")
	}
	return nil
}
func main() {
	x := 10
    
  // 局部变量 err 作用域覆盖所有分支。
	if err := do(x); err == nil {
    // 正常逻辑。
		x++
		println(x)
  } else {
    // 错误处理。
		log.Fatalln(err)
	}
}
func main() {
	x := 10
	err := do(x)
    // 错误处理是对正常逻辑的补充。
    // 是阅读时可被 “忽略” 的片段。
	if err != nil {
		log.Fatalln(err)
	}
	
	x++
	println(x)
}

# Switch

利用 switch 语句进行多路匹配。

支持初始化语句。
从上到下、从左到右顺序匹配。
全部匹配失败,执行 default。
隐式 break 中断。
相邻空 case 不构成多条件匹配。
匹配条件中,不能有重复常量值,变量则按次序匹配。

func main() {
	a, b, c := 1, 2, 3
    
	switch x := 5; x {
	case a, b:              // OR
		println("a | b")
	case c:
		println("c")
	case 4:
		println("d")
	default:
		println(x)
	}
}
func main() {
	switch x := 1; x {
	case 0:              // 隐式 break 中断。
	case 1:
	case 2:
		println("b")
	}
}
func main() {
	switch x := 1; x {
	case 1:
	case 1, 2:  // duplicate case 1 (constant of type int)
	}
}
func main() {
	a, b := 1, 2
	x := a
	switch x {
	case b, a: println("b, a")
	case a   : println("a")
	}
}
// b, a

执行 fallthrough

按源码顺序。
不检查后续条件。
必须是当前块(case)结尾,且后面有其他块。
某些时候,省略条件变量以替换分支过多的 if 语句。

func main() {
	switch x := 5; x {
	case 5:
		println("5")
		fallthrough    // 不判断 case 6 匹配条件,直接执行下一个 case 块
	case 6:
		println("6")
	case 7:
		println("7")
  default:
    println("default")
	}
}
// 5
// 6
func main() {
	switch x := 5; x {
	default:
		println("0")
	case 5:
		println("5")
		// 按源码顺序,所以上面的 default 不执行。
		// 除非将 default 挪到下面。
		// fallthrough
		// ~~~~~~~~~~~ cannot fallthrough final case in switch
	}
}
func main() {
	switch x := 5; x {
	case 5:
		x += 10
		// if x >= 15 {
		// 	fallthrough   // fallthrough statement out of place
		// }
		if x < 15 { break }
		fallthrough
	case 6:
	}
}
func main() {
	switch x := 1; {        // switch x := 5; true { ... }
	case x == -1, x == 1:   // OR, ||
		println("a")
	case x > 1 && x <= 10:
		println("a")
	case x > 10:
		println("b")
	default:
		println("z")
	}
}

# 循环语句

仅 for 一种循环语句,常用方式都能支持。

func main() {
	
	for i := 0; i < 3; i++ {
	}
  x := 0
	for x < 10 {
		x++
	}
	for {
		break
	}
}

初始化语句仅执行一次。
条件表达式中的函数被多次调用,或被内联优化。
所定义变量,在整个循环周期内重复使用。

func count() int {
	print("count.")
	return 3
}
func main() {
	// 初始化语句仅执行一次。
	for i, c := 0, count(); i < c; i++ {
		println("a", i)
	}
    
	c := 0
	// 条件表达式,多次调用。
	for c < count() {
		println("b", c)
		c++
	}
}

# Range

for...range 迭代数组、切片、字典等。


  type              1st value      2nd value
-----------------+--------------+--------------+-----------------
  string            index          s[index]       unicode, rune
  array, slice      index          v[index]
  map               key            value
  channel           element
func main() {
	data := []int{10, 11, 12}
	for i, s := range data {
		println(i, s)
	}
}
/*
0 10
1 11
2 12
*/
// 允许返回单值,或用 _ 忽略。
func main() {
	data := []int{10, 11, 12}
	for i := range data {
		println(i)
	}
	for _, v := range data {
		println(v)
	}
	for range data {
		println("a")
	}
}

定义的局部变量会重复使用。

func main() {
	data := []int{ 1, 2, 3 }
    
	for i, s := range data {
		println(&i, &s)
	}
}
/*
0xc000032748 0xc000032740
0xc000032748 0xc000032740
0xc000032748 0xc000032740
*/

range 会复制目标数据。受直接影响的是数组,改用切片或指针。

func main() {
	data := [...]int{1, 2, 3}
	for i, x := range data {
		if i == 0 {
			data[0] += 100
			data[1] += 200
			data[2] += 300
		}
		println(x, data[i])
	}
}
/*
1 101
2 202
3 303
$ go tool objdump -S -s "main\.main" ./test
func main() {
	data := [...]int{1, 2, 3}
	MOVQ $0x1, 0x20(SP)	
	MOVQ $0x2, 0x28(SP)	
	MOVQ $0x3, 0x30(SP)	
	for i, x := range data {
		
	MOVQ $0x1, 0x38(SP)	
	MOVQ $0x2, 0x40(SP)	
	MOVQ $0x3, 0x48(SP)	
*/

如目标表达式是函数调用,仅执行一次。

//go:noinline
func data() []int {
	println("data")
	return []int{10, 20, 30}
}
func main() {
	for _, x := range data() {
		println(x)
	}
}
/*
data
10
20
30
*/

# 跳转

# Goto

讨伐已久的 goto ,在运行时源码中不算稀客。

虽然某些设计模式可用来消除 goto 语句,但在性能优先的场合,它能发挥积极作用。

使用前,先定义标签。

标签区分大小写。
未使用的标签会引发编译错误。
不能跳转到其他函数,或内层代码块。
不能导致变量定义跳跃。

func main() {
// start:
// label start defined and not used
	for i := 0; i < 3; i++ {
		if i > 1 {
			goto exit
		}
		println(i)
	}
exit:
	println("exit.")
}
func test() {
	goto test
test:
}
func main() {
	for{
	loop:
	}
	goto test   // label test not defined
	goto loop   // goto loop jumps into block
}
func main() {
	// goto done
	// ~~~~~~~~~ goto done jumps over declaration of v
	v := 0
done:
	println(v)
}

# 中断

与定点跳转不同,中断结束当前或指定语句块。

break :终止 switch/for/select 语句块。
continue :仅用于 for,进入下轮循环。

func main() {
	for i := 0; i < 10; i++ {
        
		if i % 2 == 0 {
            continue     // 立即进入下轮循环。(goto next)
		}
		if i > 5 {
			break        // 立即终止整个循环。
		}
        
		println(i)
	}
}
/*
1
3
5
*/

配合标签,在多层嵌套中指定目标。

func main() {
    
outer:
	for x := 0; x < 10; x++ {
        
inner:		
		for y := 0; y < 10; y++ {
			if x % 2 == 0 {
				continue outer
			}
			if y > 3 {
				println()
				break inner
			}
            
			print(x, ":", y, " ")
		}
	}
}
/*
1:0 1:1 1:2 1:3 
3:0 3:1 3:2 3:3 
5:0 5:1 5:2 5:3 
7:0 7:1 7:2 7:3 
9:0 9:1 9:2 9:3
*/
func main() {
    
start:
	switch 1 {
	case 1:
		println(1)
        
		for {
			break start
		}
		println(2)
	}
	println(3)
}
// 1
// 3
更新于
-->