[翻译] effective go 之 Control structures

pengfei_xue · · 3708 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Control structures

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; 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中没有while循环 switch用起来更加灵活 if和switch可以有可选的初始化语句 Go增加了新的控制结构 包含类型switch常用来判断接口的动态类型 还有多路通信复用的select(channel里会使用到这个)语法上也有些细小的差别 控制结构并不需要圆括号 但是花括号是必不可少的


If

In Go a simple if looks like this:

Go中的if语句:

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.

强制添加花括号鼓励大家把简单的if语句也写成多行 这是比较好的代码风格 特别是if语句里有return 或者 break

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

if和switch可以有初始化语句 通常会定义局部变量

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.

在Go的标准库源码中 可以发现else经常是被省略掉的 特别是if语句中以break continue goto或者return结尾

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.

下面这段代码 展示了Go中常用的错误判断 以及处理方式 由于产生错误后 直接就返回了 所以这里并不需要else语句

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


Redeclaration 重复申明

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) // 这句定义了两个变量f err
d, err := f.Stat() // 从f中读数据

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.

初略地看 上面两句第一了d和err 但是有没有注意到err在上面两句中出现了 这种情况是合法的 err只是在第一句中被定义 在第二句中仅仅给err赋值 也就是说 f.Stat使用了已定义的err变量 仅仅给了它一个新的值

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

符合以下条件时  := 声明一个已经定义过的变量v是合法的:

  • 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),
  • 第二次声明和v在同样的作用域下(如果v是在函数外定义的 函数体内再次定义v 就会创建一个新的变量v 并且覆盖掉函数外的v)
  • the corresponding value in the initialization is assignable to v, and
  • 新的值对于v来说是可以接受的 类型必须一致
  • there is at least one other variable in the declaration that is being declared a new.
  • 第二次声明时 必须至少和一个新的变量一起被声明

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.

这个奇葩特性绝对体现了实用主义 可以更加方便地使用err 


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 并且它把C中的while给吞并了 Go中没有do while这样的结构 for有三种形式 只有一种会用到分号

// Like a C for 和C中for循环类似 但是这里没有圆括号哦
for init; condition; post { }

// Like a C while C中的while
for condition { }

// Like a C for(;;) C中的死循环
for { }

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

简短的声明形式 可以方便地在循环开始的位置定义游标变量

sum := 0
for 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.

如果你想遍历数组 slice 字符串 map 或者从channel里读数据 你可以使用range

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:

range返回key(或者游标位置)以及相应的值 比如range一个数组['a','b','c'] range的结果就是

1 ‘a'

2 'b'

3 'c' 如果你只想要range的第一个元素(上面的1 2 3)可以这样写:

for key := range m {
    if expired(key) {
        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:

如果你只想要第二个元素(上面的'a' 'b' 'c')可以使用空标识符 _, 丢弃range返回的第一个元素:

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


For strings, the range does more work for you, breaking out individual Unicode characters by parsing the UTF-8. Erroneous encodings consume one byte and produce the replacement rune U+FFFD. The loop

在遍历字符串时 Go做了很多幕后的工作 对于每个Unicode字符使用UTF8解码 无法解码的字符占用一个字节 并且使用U+FFFD代替【U+FFFD就是"�" (U+FFFD)】 下面这个循环

for pos, char := range "日本語" {
    fmt.Printf("character %c starts at byte position %d\n", char, pos)
}

会输出:

character 日 starts at byte position 0
character 本 starts at byte position 3
character 語 starts at byte position 6


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.

Go没有逗号操作符 ++和--是语句而不是表达式 如果你想同时给多个变量赋值 你只能:

// Reverse a
for 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 theswitch 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语句从头开始执行 直到和某个case匹配 所以可以使用switch来改写if-else-if-else

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.

Go的switch并不像C那样 如果不加break 会导致和某个case匹配后 继续执行之后的case 但是可以使用逗号分割的列表来达到此目的

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


Here's a comparison routine for byte slices that uses two switch statements:

下面这个函数 使用两个switch来比较两个byte slice

// 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 > b
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.

switch可以用来判断接口的动态类型  type switch使用类型断言 T.(type) T是某个接口变量 这个用法只有在switch中才是合法的

switch t := interfaceValue.(type) {
default:
    fmt.Printf("unexpected type %T", t)  // %T prints type
case bool:
    fmt.Printf("boolean %t\n", t)
case int:
    fmt.Printf("integer %d\n", t)
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t)
case *int:
    fmt.Printf("pointer to integer %d\n", *t)
}


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:pengfei_xue

查看原文:[翻译] effective go 之 Control structures

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3708 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传