## 控制结构

The control structures of Go are related to those of C but differ in important ways. There is no do or while loop, only a slightly generalized for; switch is more flexible; if and switch accept an optional initialization statement like that of for; break and continue statements take an optional label to identify what to break or continue; and there are new control structures including a type switch and a multiway communications multiplexer, select. The syntax is also slightly different: there are no parentheses and the bodies must always be brace-delimited.

Go 中的结构控制与 C 有许多相似之处，但其不同之处才是独到之处。 Go 不再使用 do 或 while 循环，只有一个更通用的 for；switch 要更灵活一点；if 和 switch 像 for 一样可接受可选的初始化语句； 此外，还有一个包含类型选择和多路通信复用器的新控制结构：select。 其语法也有些许不同：没有圆括号，而其主体必须始终使用大括号括住。

### If

In Go a simple if looks like this:

if x > 0 {    return y}

Mandatory braces encourage writing simple if statements on multiple lines. It’s good style to do so anyway, especially when the body contains a control statement such as a return or break.

Since if and switch accept an initialization statement, it’s common to see one used to set up a local variable.

if err := file.Chmod(0664); err != nil {    log.Print(err)    return err}

In the Go libraries, you’ll find that when an if statement doesn’t flow into the next statement—that is, the body ends in break, continue, goto, or return—the unnecessary else is omitted.

f, err := os.Open(name)if err != nil {    return err}codeUsing(f)

This is an example of a common situation where code must guard against a sequence of error conditions. The code reads well if the successful flow of control runs down the page, eliminating error cases as they arise. Since error cases tend to end in return statements, the resulting code needs no else statements.

f, err := os.Open(name)if err != nil {    return err}d, err := f.Stat()if err != nil {    f.Close()    return err}codeUsing(f, d)

### 重新声明与再次赋值

An aside: The last example in the previous section demonstrates a detail of how the := short declaration form works. The declaration that calls os.Open reads,

f, err := os.Open(name)

This statement declares two variables, f and err. A few lines later, the call to f.Stat reads,

d, err := f.Stat()

which looks as if it declares d and err. Notice, though, that err appears in both statements. This duplication is legal: err is declared by the first statement, but only re-assigned in the second. This means that the call to f.Stat uses the existing err variable declared above, and just gives it a new value.

In a := declaration a variable v may appear even if it has already been declared, provided:

• this declaration is in the same scope as the existing declaration of v (if v is already declared in an outer scope, the declaration will create a new variable §),
• the corresponding value in the initialization is assignable to v, and
• there is at least one other variable in the declaration that is being declared anew.
• 本次声明与已声明的 v 处于同一作用域中（若 v 已在外层作用域中声明过，则此次声明会创建一个新的变量 §），
• 在初始化中与其类型相应的值才能赋予 v，且
• 在此次声明中至少另有一个变量是新声明的。

This unusual property is pure pragmatism, making it easy to use a single err value, for example, in a long if-else chain. You’ll see it used often.

§ It’s worth noting here that in Go the scope of function parameters and return values is the same as the function body, even though they appear lexically outside the braces that enclose the body.

§ 值得一提的是，即便 Go 中的函数形参和返回值在词法上处于大括号之外， 但它们的作用域和该函数体仍然相同。

### For

The Go for loop is similar to—but not the same as—C’s. It unifies for and while and there is no do-while. There are three forms, only one of which has semicolons.

Go 的 for 循环类似于 C，但却不尽相同。它统一了 for 和 while，不再有 do-while 了。它有三种形式，但只有一种需要分号。

// Like a C forfor init; condition; post { }// Like a C whilefor condition { }// Like a C for(;;)for { }
// 如同 C 的 for 循环for init; condition; post { }// 如同 C 的 while 循环for condition { }// 如同 C 的 for(;;) 循环for { }

Short declarations make it easy to declare the index variable right in the loop.

sum := 0for i := 0; i < 10; i++ {    sum += i}

If you’re looping over an array, slice, string, or map, or reading from a channel, a range clause can manage the loop.

for key, value := range oldMap {    newMap[key] = value}

If you only need the first item in the range (the key or index), drop the second:

for key := range m {    if key.expired() {        delete(m, key)    }}

If you only need the second item in the range (the value), use the blank identifier, an underscore, to discard the first:

sum := 0for _, value := range array {    sum += value}

The blank identifier has many uses, as described in a later section.

For strings, the range does more work for you, breaking out individual Unicode code points by parsing the UTF-8. Erroneous encodings consume one byte and produce the replacement rune U+FFFD. (The name (with associated builtin type) rune is Go terminology for a single Unicode code point. See the language specification for details.) The loop

for pos, char := range "日本 \ x80 語" { // \x80 is an illegal UTF-8 encoding    fmt.Printf("character %#U starts at byte position %d\n", char, pos)}

prints

character U+65E5 '日' starts at byte position 0character U+672C '本' starts at byte position 3character U+FFFD '�' starts at byte position 6character U+8A9E '語' starts at byte position 7
for pos, char := range "日本\x80語" { // \x80 是个非法的UTF-8编码    fmt.Printf("字符 %#U 始于字节位置 %d\n", char, pos)}

字符 U+65E5 '日' 始于字节位置 0字符 U+672C '本' 始于字节位置 3字符 U+FFFD '�' 始于字节位置 6字符 U+8A9E '語' 始于字节位置 7

Finally, Go has no comma operator and ++ and — are statements not expressions. Thus if you want to run multiple variables in a for you should use parallel assignment (although that precludes ++ and —).

// Reverse afor i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {    a[i], a[j] = a[j], a[i]}
// 反转 afor i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {    a[i], a[j] = a[j], a[i]}

### Switch

Go’s switch is more general than C’s. The expressions need not be constants or even integers, the cases are evaluated top to bottom until a match is found, and if the switch has no expression it switches on true. It’s therefore possible—and idiomatic—to write an if-else-if-else chain as a switch.

Go 的 switch 比 C 的更通用。其表达式无需为常量或整数，case 语句会自上而下逐一进行求值直到匹配为止。若 switch 后面没有表达式，它将匹配 true，因此，我们可以将 if-else-if-else 链写成一个 switch，这也更符合 Go 的风格。

func unhex(c byte) byte {    switch {    case '0' <= c && c <= '9':        return c - '0'    case 'a' <= c && c <= 'f':        return c - 'a' + 10    case 'A' <= c && c <= 'F':        return c - 'A' + 10    }    return 0}

There is no automatic fall through, but cases can be presented in comma-separated lists.

switch 并不会自动下溯，但 case 可通过逗号分隔来列举相同的处理条件。

func shouldEscape(c byte) bool {    switch c {    case ' ', '?', '&', '=', '#', '+', '%':        return true    }    return false}

Although they are not nearly as common in Go as some other C-like languages, break statements can be used to terminate a switch early. Sometimes, though, it’s necessary to break out of a surrounding loop, not the switch, and in Go that can be accomplished by putting a label on the loop and”breaking” to that label. This example shows both uses.

Loop:    for n := 0; n < len(src); n += size {        switch {        case src[n] < sizeOne:            if validateOnly {                break            }            size = 1            update(src[n])        case src[n] < sizeTwo:            if n+1 >= len(src) {                err = errShortInput                break Loop            }            if validateOnly {                break            }            size = 2            update(src[n] + src[n+1]<<shift)        }    }

Of course, the continue statement also accepts an optional label but it applies only to loops.

To close this section, here’s a comparison routine for byte slices that uses two switch statements:

// Compare returns an integer comparing the two byte slices,// lexicographically.// The result will be 0 if a == b, -1 if a < b, and +1 if a > bfunc Compare(a, b []byte) int {    for i := 0; i < len(a) && i < len(b); i++ {        switch {        case a[i] > b[i]:            return 1        case a[i] < b[i]:            return -1        }    }    switch {    case len(a) > len(b):        return 1    case len(a) < len(b):        return -1    }    return 0}

// Compare 按字典顺序比较两个字节切片并返回一个整数。// 若 a == b，则结果为零；若 a < b；则结果为 -1；若 a > b，则结果为 +1。func Compare(a, b []byte) int {    for i := 0; i < len(a) && i < len(b); i++ {        switch {        case a[i] > b[i]:            return 1        case a[i] < b[i]:            return -1        }    }    switch {    case len(a) > len(b):        return 1    case len(a) < len(b):        return -1    }    return 0}

### 类型选择

A switch can also be used to discover the dynamic type of an interface variable. Such a type switch uses the syntax of a type assertion with the keyword type inside the parentheses. If the switch declares a variable in the expression, the variable will have the corresponding type in each clause. It’s also idiomatic to reuse the name in such cases, in effect declaring a new variable with the same name but a different type in each case.

var t interface{}t = functionOfSomeType()switch t := t.(type) {default:    fmt.Printf("unexpected type %T", t)       // %T prints whatever type t hascase bool:    fmt.Printf("boolean %t\n", t)             // t has type boolcase int:    fmt.Printf("integer %d\n", t)             // t has type intcase *bool:    fmt.Printf("pointer to boolean %t\n", *t) // t has type *boolcase *int:    fmt.Printf("pointer to integer %d\n", *t) // t has type *int}

switch 也可用于判断接口变量的动态类型。如 类型选择 通过圆括号中的关键字 type 使用类型断言语法。若 switch 在表达式中声明了一个变量，那么该变量的每个子句中都将有该变量对应的类型。在这些 case 中重用一个名字也是符合语义的，实际上是在每个 case 里声明了一个不同类型但同名的新变量。

var t interface{}t = functionOfSomeType()switch t := t.(type) {default:    fmt.Printf("unexpected type %T", t)       // %T 输出 t 是什么类型case bool:    fmt.Printf("boolean %t\n", t)             // t 是 bool 类型case int:    fmt.Printf("integer %d\n", t)             // t 是 int 类型case *bool:    fmt.Printf("pointer to boolean %t\n", *t) // t 是 *bool 类型case *int:    fmt.Printf("pointer to integer %d\n", *t) // t 是 *int 类型}