言小白年初许下了关于golang的愿望,这是来践约的。
另外,本小抄无扫盲功效,如需从零开始,参见A Tour of Go
控制结构
重新声明和重新赋值
1 | f, err := os.Open(name) |
在:=
声明中,变量v
即使已经声明过,也可以出现在:=
的左端,只要它满足以下: * 声明和变量v
的现有声明位于同一个作用域(如果v
在外部作用域中已经被声明了,那么,该声明将会创建一个新的变量。这里需要注意的是,在Go中,函数参数和函数返回值的作用域和函数体相同,即使在语义上,它们出现在函数花括号外部) * 初始化中相应的值是可以被赋予v的(注意:重新声明并非引进一个新变量,而是可以看成重新赋值),并且 * 在此声明中,至少存在一个新声明的变量
For
三种用法: 1
2
3
4
5
6
7
8// 就像C的for
for init; condition; post {}
// 就像C的while
for condition {}
// 就像C的f(;;)
for {}1
2
3
4sum := 0
for i := 0; i < 10; i++ {
sum += i
}range
: 1
2
3
4
5
6
7
8
9
10
11
12for key, value := range oldMap {
newMap[key] = value
}
// 当仅需要第一个项的时候,也可以把第二个省略掉
for key := range m {
delete(m, key)
}
// 当只需要第二项的时候,使用_
sum := 0
for _, value := rang array {
sum += value
}
Switch
1 | func unhex(c byte) byte { |
Go的switch语句比C
跟广泛一些。 * 表达式无需是常量或者整型。如果switch没有表达式,那么默认为true。 * 对于case
的评估是自上而下直至找到一个匹配的 * 可以使用break
。break
默认情况下退出当前的switch
。如果switch
语句外层嵌套了for
循环,而要退出外层的for
循环的话,那么可以使用标签,即在循环上面放一个标签(例如:Loop
),然后直接break
此标签(例如:break Loop
)。当然,continue
也接受一个可选的标签作为参数,但是它仅对循环有效
type-switch
用来发现一个接口变量的动态类型。 1
2
3
4
5
6
7
8
9
10
11
12
13
14var a interface{}
a = 3
// 下面定义了一个变量v,这个变量在各个case语句中都有对应的类型。
// 在这种情况下,建议重用变量名,即switch v := v.(type)。这样实际上,在每个case中声明了一个具有相同名称,但是不同类型的新变量。
switch v := a.(type) {
case bool:
// v类型为bool
fmt.Printf("%T %t\n", v, v)
case int:
// v类型为int
fmt.Printf("%T %d\n", v, v)
default:
fmt.Printf("unexpected type %T\n", v)
}
函数
多值返回
和Python不一样,对于go的多值返回,在指定输入参数后,需要指定要返回的值列表。例如: 1
2
3func test(a int, b string) (string, error){
//...
}too many arguments to return
多值返回的好处在于: * 带错误返回 * 避免的需要传递一个指针来返回一个值
命名返回参数
在Go中,可以命名一个函数的返回参数,并将其作为常规变量使用(就如函数接收参数一样)。此时,返回参数会被初始化为其类型的零值。如果函数直接return
(不带任何参数),那么返回参数的当前值就会被当成返回值返回。
不强制命名返回参数,但是这能让代码更短更清晰。
missing return at end of function
在Go 1.1,无需总是在函数末端添加return语句,但是需要有个结束声明。
推荐阅读: - return - Terminating statement
Defer
Go会在执行defer
语句的函数返回之前立即执行defer
语句后面的函数。常见的使用场景为解锁一个mux,或者关闭文件。 1
2
3
4
5
6
7
8func Contents(filename string)(string error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
....
}Close()
这样的函数使用defer
有两个好处: * 保证你永远不会忘记做清理操作 * 将其放在诸如Open()
这样的函数后面,使得代码更清晰
注意:在defer func()
中,函数func
的参数(如果函数是个方法,那么也包含接收者)会在defer
执行的时候计算,而不是在函数func
执行的时候计算。这样避免了在函数执行的时候变量值的改变,也意味着单个defer
调用可以defer多个函数执行。例如: 1
2
3
4for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
// 输出:4 3 2 1 0