if…else…

  1. if boolean_expression {
  2. // 分支1
  3. } else if boolean_expression {
  4. // 分支2
  5. } else {
  6. // 分支3
  7. }

if语句“快乐路径”原则

从可读性上来看,单分支结构要优于二分支结构,二分支结构又优于多分支结构。编码中要减少多分支结构甚至是二分支结构的使用,这有助于编写出优雅、简洁、易读易维护且不易错的代码。用以下示例来体会一下:

  1. //伪代码段1:
  2. func doSomething() error {
  3. if errorCondition1 {
  4. // some error logic
  5. ... ...
  6. return err1
  7. }
  8. // some success logic
  9. ... ...
  10. if errorCondition2 {
  11. // some error logic
  12. ... ...
  13. return err2
  14. }
  15. // some success logic
  16. ... ...
  17. return nil
  18. }
  19. // 伪代码段2:
  20. func doSomething() error {
  21. if successCondition1 {
  22. // some success logic
  23. ... ...
  24. if successCondition2 {
  25. // some success logic
  26. ... ...
  27. return nil
  28. } else {
  29. // some error logic
  30. ... ...
  31. return err2
  32. }
  33. } else {
  34. // some error logic
  35. }
  36. }

很明显,伪代码1的逻辑更容易理解,也更简洁。Go社区把这种if语句的使用方式称为if语句的“快乐路径”原则,所谓“快乐路径”也就是成功逻辑的代码执行路径,它的特点是这样的:

  • 仅使用单分支控制结构;
  • 当布尔表达式求值为false时,也就是出现错误时,在单分支中快速返回;
  • 正常逻辑在代码布局上始终“靠左”,这样读者可以从上到下一眼看到该函数正常逻辑的全貌;
  • 函数执行到最后一行代表一种成功状态

for

  1. for 初始语句;条件表达式;结束语句 {
  2. 循环体代码
  3. }
  4. for i := 0; i < 10; i++ {
  5. fmt.Println(i)
  6. }
  7. // 无限循环有3种方式,推荐第一种
  8. for {
  9. ...
  10. }
  11. for true {
  12. ...
  13. }
  14. for ; ; {
  15. ...
  16. }

for range

在go语言中,对于数组、切片、map以及channel等数据结构,用for range遍历起来会更方便

  1. //遍历切片
  2. s := []int{1, 2, 3}
  3. for i, v := range s {
  4. fmt.Println(i, v)
  5. }
  6. //遍历map
  7. m := make(map[string]int)
  8. m["a"] = 1
  9. m["b"] = 2
  10. m["c"] = 3
  11. for k, v := range m {
  12. fmt.Println(k, v)
  13. }

for range 有几个常见的变种,以切片为例进行分析:

  1. 不关心元素的值时,可以省略代表元素值的变量v,只声明代表下标值的变量i

    1. s := []int{1, 2, 3}
    2. for i := range s {
    3. fmt.Println(i) // 0 1 2
    4. }
  2. 不关心下标,只关心元素值,那么可以用空标识符替代代表下标值的变量,这个空标识符不能省略,通常用下划线”_”表示

    1. s := []int{1, 2, 3}
    2. for _, v := range s {
    3. fmt.Println(v) // 1 2 3
    4. }
  3. 既不关心下标,也不关心元素值

    1. s := []int{1, 2, 3}
    2. for range s {
    3. fmt.Println("test") // 打印3遍test
    4. }

switch

  1. n := 2
  2. switch n {
  3. case 1:
  4. fmt.Println("n=1")
  5. case 2:
  6. fmt.Println("n=2") //
  7. default:
  8. fmt.Println("default")
  9. }

探索switch执行次序

  1. package main
  2. import "fmt"
  3. func case1() string {
  4. fmt.Println("case1")
  5. return "aaa"
  6. }
  7. func case2() string {
  8. fmt.Println("case2")
  9. return "return值相同则匹配成功, switch语句退出"
  10. }
  11. func case3() string {
  12. fmt.Println("case3")
  13. return "ccc"
  14. }
  15. func switchexpr() string {
  16. fmt.Println("switchexpr")
  17. return "return值相同则匹配成功, switch语句退出"
  18. }
  19. func main() {
  20. switch switchexpr() {
  21. case case1():
  22. fmt.Println("main1")
  23. case case2():
  24. fmt.Println("main2")
  25. case case3():
  26. fmt.Println("main3")
  27. default:
  28. fmt.Println("default")
  29. }
  30. }

示例程序的执行结果为:

  1. switchexpr
  2. case1
  3. case2
  4. main2

上例中先拿到switchexpr()函数的返回值,再逐一与case语句中的函数返回值进行匹配,直到case语句中的某一函数的返回值与switchexpr()函数一致,打印对应逻辑,然后退出switch语句。示例中到case2函数就完成了匹配,所以其后面case3函数、default都不会再执行。

case语句支持表达式列表

  1. func checkWorkday(a int) {
  2. switch a {
  3. case 1, 2, 3, 4, 5:
  4. fmt.Println("it is a work day")
  5. case 6, 7:
  6. fmt.Println("it is a weekend day")
  7. default:
  8. println("default")
  9. }
  10. }

case1~case5匹配成功后,执行的都是case5中的代码逻辑,case6~case7匹配成功之后,执行的都是case7中的代码逻辑。

fallthough

如果需要强制执行下一个case的代码逻辑,可以显示使用Go提供的关键字fallthough的switch来实现。继续用前面的示例来举例:

  1. package main
  2. import "fmt"
  3. func case1() string {
  4. fmt.Println("case1")
  5. return "aaa"
  6. }
  7. func case2() string {
  8. fmt.Println("case2")
  9. return "return值相同则匹配成功, switch语句退出"
  10. }
  11. func case3() string {
  12. fmt.Println("case3")
  13. return "ccc"
  14. }
  15. func switchexpr() string {
  16. fmt.Println("switchexpr")
  17. return "return值相同则匹配成功, switch语句退出"
  18. }
  19. func main() {
  20. switch switchexpr() {
  21. case case1():
  22. fmt.Println("main1")
  23. case case2():
  24. fmt.Println("main2")
  25. fallthrough
  26. case case3():
  27. fmt.Println("main3") // 如果没有fallthrough关键字,代码逻辑不会执行到这个case3
  28. default:
  29. fmt.Println("default")
  30. }
  31. }

示例程序的执行结果为:

  1. switchexpr
  2. case1
  3. case2
  4. main2
  5. main3

有了fallthrough关键字,在执行完case2的语句后,switch语句没有退出,而是继续执行了下一个case,另外由于fallthrough的存在,Go不会执行case3函数,而是直接执行case3对应的代码分支。

另外还有一点要注意的是,如果某个case语句已经是switch语句中的最后一个case,并且它的后面也没有default分支,那么这个case中就不能再使用fallthrough,否则编译报错。