一 终止语句

终止语句可以阻止当前块中其后出现的所有语句的执行,规则如下:

  1. returngoto 语句。
  2. 调用内置函数panic
  3. 语句列表以终止语句结束的块。
  4. if语句,其中:

    • else分支存在,并且
    • 两个分支都是终止语句。
  5. for语句,其中:

    • 没有break语句引用for语句,并且
    • 循环条件不存在
  6. switch语句,其中:

    • 没有break语句引用switch语句
    • 存在default子句,并且
    • 在每种case下,包括default的语句,以终止语句结束,或者可能标记为fallthrough语句。
  7. select语句,其中:

    • 没有break语句引用select语句,并且
    • 在每种case下,包括default语句(如果存在),以终止语句结束。
  8. 标签语句标记终止的语句

除此之外,所有其他语句都不会终止。

如果语句列表不为空,并且最后一个非空语句终止,则该列表终止。

二 标签语句

标签语句可能是gotobreakcontinue语句的目标。

Label:
    Statement

三 表达式语句

除特定的内建函数外,函数和方法调用,还有接收操作可以出现在语句上下文中。这些语句可以用括号括起来。
语句上下文中不允许使用以下内置函数:

append cap complex imag len make new real
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof

四 赋值

赋值使用=符号。每个左侧操作数必须是可寻址的、映射索引表达式或空白标识符(仅限=赋值)。

x = 1
*p = f()
a[i] = 23
(k) = <-ch  // 同: k = <-ch

对于赋值操作x op= y,其中op是一个二元算数运算符,则其等价于x = x op y,但是对于x只进行一次求值。op=是单个标记。在赋值操作中,左右两边的表达式列表必须只包含一个单值表达式,且左侧表达式不能是空白标识符。

a[i] <<= 2
i &^= 1<<n

元组赋值将多值运算的各个元素分配给变量列表。有两种形式:
第一种,右侧操作数是单个多值表达式,例如函数调用,信道或映射操作,或类型断言。左侧操作数的数量必须与值的数量匹配。例如,如果f是一个返回两个值的函数。

x, y = f()

将第一个值赋值给x,第二个赋值给y

第二种,左侧操作数个数必须与右侧表达式个数相等,其中右侧表达式必须是单值的,且右侧第n个表达式将赋值给左侧第n个变量:

one, two, three = '一', '二', '三'

空白标识符提供了一种方式来忽略赋值过程中右侧的值:

_ = x       // 对x求值,但忽略结果
x, _ = f()  // 对f()求值,但忽略第二个值

赋值分两个阶段进行。首先,左侧的索引表达式和指针重定向操作(包括选择器中的隐式指针重定向)的操作数和右侧的表达式都按正常的顺序进行求值。其次,赋值按从左到右的顺序进行。

a, b = b, a  // 交换a和b

x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2  // 设置 i = 1, x[0] = 2

i = 0
x[i], i = 2, 1  // 设置 x[0] = 2, i = 1

x[0], x[0] = 1, 2  // 设置 x[0] = 1, 然后 x[0] = 2 (所以最后 x[0] == 2)

x[1], x[3] = 4, 5  // 设置 x[1] = 4, 然后紧接着设置 x[3] = 5.

type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7  // 设置 x[2] = 6, 然后紧接着设置 p.x = 7

i = 2
x = []int{3, 5, 7}
for i, x[i] = range x {  // 设置 i, x[2] = 0, x[0]
    break
}
// 该循环之后, i == 0 且 x == []int{3, 5, 3}

在赋值中,每个值必须可分配给它所分配的操作数的类型,另外以下是特例:

  1. 可以将任何有类型的值分配给空白标识符。
  2. 如果将无类型常量分配给接口类型的变量或空白标识符,则首先将常量隐式转换为其默认类型。
  3. 如果将无类型布尔值分配给接口类型的变量或空白标识符,则首先将其隐式转换为bool类型。

五 Return语句

函数F中的return语句将终止F的执行,并可选地提供一个或多个结果值。 任何由F推迟的函数都将在F返回其调用者之前执行。

在没有结果类型的函数中,return语句不能指定任何结果值:

func noResult() {
    return
}

有三种方式从有结果值的函数中返回值:

  1. 返回值可以在return中明确指出。每个表达式必须是单值的,且可赋值给函数的结果类型:

    func simpleF() int {
        return 2
    }
    
    func complexF1() (re float64, im float64) {
        return -7.0, -4.0
    }
  2. return语句中的表达式列表可以是对多值函数的单个调用。就好像从该函数返回的每个值都分配给具有相应值类型的临时变量,然后是列出这些变量的return语句,此时应用前一个案例的规则。

    func complexF2() (re float64, im float64) {
        return complexF1()
    }
  3. 如果函数的结果类型指定其结果参数的名称,则表达式列表可以为空。结果参数充当普通局部变量,并且函数可以根据需要为它们分配值。return语句返回这些变量的值:

    func complexF3() (re float64, im float64) {
        re = 7.0
        im = 4.0
        return
    }
    
    func (devnull) Write(p []byte) (n int, _ error) {
        n = len(p)
        return
    }

无论如何声明它们,所有的结果值在进入函数时都将初始化为其类型的零值。指定结果的return语句在执行任何推迟函数之前设置结果参数。

实现限制:如果与结果参数同名的其他实体(常量,类型或变量)在返回位置的范围内,则编译器可能会在return语句中禁止空表达式列表。

func f(n int) (res int, err error) {
    if _, err := f(n-1); err != nil {
        return  // invalid return statement: err is shadowed
    }
    return
}

六 Break语句

break语句终止在同一函数内执行最里面的forswitchselect语句。

如果有标签,则必须是包含forswitchselect语句的标签,并且该标签的执行将终止:

OuterLoop:
    for i = 0; i < n; i++ {
        for j = 0; j < m; j++ {
            switch a[i][j] {
            case nil:
                state = Error
                break OuterLoop
            case item:
                state = Found
                break OuterLoop
            }
        }
    }

七 Continue语句

continue语句表示开始下一次最内层for循环的迭代,该for循环必须在同一个函数中。
如果有标签,那么它必须包含一个for语句,并且该for循环即执行进程的标签。

RowLoop:
    for y, row := range rows {
        for x, data := range row {
            if data == endOfRow {
                continue RowLoop
            }
            row[x] = data + bias(x, y)
        }
    }

八 Goto语句

goto语句将控制转移到同一个函数内相应的标签语句。

goto Error

执行goto语句不得导致任何尚未在goto范围内的变量进入。例如,这个例子是错误的:

    goto L  // BAD
    v := 3
L:

跳转到标签L会导致跳过v的创建。

goto语句只能跳转到同一个函数中的同一个块中,不能跳到其他块,例如,这个例子也是错误的:

if n%2 == 1 {
    goto L1 // error
}
for n > 0 {
    f()
    n--
L1:
    f()
    n--
}

因为标签L1for语句的块内,但goto不在。

九 Fallthrough语句

fallthrough语句将控制转移到表达式switch语句中的下一个case子句的第一个语句。它可能仅用作此类子句中的最终非空语句。

switch n {
        case 1 :
            fmt.Println("case 1")
            fmt.Println("case 1")
        case 2 :
            fmt.Println("case 2")
            fmt.Println("case 2")
            fallthrough
        case 3 :
            fmt.Println("case 3")
            fmt.Println("case 3")
}

参考:
https://golang.org/ref/spec#Statements

文章目录