Golang – 控制语句与错误处理

文章目录

Golang中的条件控制语句省去了许多不必要的附加符号,使代码更加简洁。

for 循环

Golang只有一种循环结构:for循环,基本形式为:

for 初始化语句; 条件语句; 修饰语句 {}
  • 基本的for循环由三部分组成,它们用分号隔开:
    • 初始化语句(可选):在第一次迭代前执行
    • 条件表达式:在每次迭代前求值
    • 后置语句(可选):在每次迭代的结尾执行
  • 与其他语言不同,Golangfor语句后面的三个构成部分外没有小括号,大括号{}则是必须的。
// for 没有小括号
for i := 0; i < 10; i++ {
    fmt.Println(i)
}
// for 初始化语句和后置语句是可选的。
var j int = 0
for j < 10 {
    fmt.Println(j)
    j++
}
// 如果省略循环条件,则无限循环
for {
  fmt.Println("Running...")
  time.Sleep(time.Millisecond * 500)
}
  • 循环控制语句可以控制循环体内语句的执行过程:
    • break语句:经常用于中断当前for循环或跳出switch语句。
    • continue语句:跳过当前循环的剩余语句,然后继续进行下一轮循环。
    • goto语句:将控制转移到被标记的语句。

for-range 遍历

数组、stringslicemapchannel等都可以使用for-range语句遍历。

slice := []int{1, 2, 3, 4, 5}
for key, value := range slice {
    fmt.Println(key, value)
}
// channel 没有 key 且阻塞
channel := make(chan int, 10)
for value := range channel {
    fmt.Println(value)
}

if 判断

Golangif判断语句与for循环类似,表达式外无需小括号,而大括号{}则是必须的。

if condition1 {
    // do something 
} else if condition2 {
    // do something else    
} else {
    // default
}

for一样,if语句可以在条件表达式前执行一个简短语句。该语句声明的变量作用域仅在 if 之内。

package main
import (
  "fmt"
  "math"
)
func pow(x, n, lim float64) float64 {
  if v := math.Pow(x, n); v < lim {
    return v
  }
  return lim
}
func main() {
  fmt.Println(
    pow(3, 2, 10),  // 9
    pow(3, 3, 20),  // 20
  )
}

switch 分支

  • switch语句是编写一连串if-else语句的简便方法。一个case值等于条件表达式的子句且case值无需为常量,且取值不限于整数。
  • Golang只会运行选定的case,与其他语言相比,相当于为每个case后面自动添加了break语句。Golang除非通过fallthrough语句继续执行后续分支的代码,否则分支会自动终止。
  • switchcase语句从上到下顺次执行,直到匹配成功时停止。
k := 3
switch k {
    case 1: fmt.Println("case 1");
    case 2: fmt.Println("case 2");
    case 3: fmt.Println("case 3"); fallthrough;
    case 4: fmt.Println("case 4");
    case 5: fmt.Println("case 5");
    default: fmt.Println("default case")
}

运行结果为:

case 3
case 4

defer 推迟

  • defer语句会将函数推迟到外层函数返回之后执行。
  • 推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
  • 推迟调用的函数调用会被压入一个栈中(LIFO:后进先出)。 当外层函数返回时,被推迟的调用会按照后进先出的顺序调用。
package main
import "fmt"
func main() {
	fmt.Println("counting")
	for i := 0; i < 10; i++ {
		defer fmt.Printf("%d\t", i)
	}
	fmt.Println("done")
}

运行结果为:

counting
done
9	8	7	6	5	4	3	2	1	0

Go 错误处理

Golang没有try/catch异常机制:不能执行抛异常操作。但是有一套defer-panic-and-recover机制,可以“捕捉”异常,也更轻量,并且只应该作为(处理错误的)最后的手段。

  • Golang有一个内建接口error类型:
type error interface {
    Error() string
}
  • 构造异常:
err := errors.New("异常信息")  // error: "异常信息"
err := fmt.Errorf("错误信息: %v", "异常信息")  // errors.New(fmt.Sprintf(...))
  • 捕捉异常:判断错误是否等于nil来进行错误处理。
func tryInt(s string) {
	i, err := strconv.ParseInt(s, 0, 64)
	if err == nil {
		fmt.Println("Value:", i)
	} else {
		fmt.Println("Error:", err)
	}
}
func main() {
	tryInt("123123")
	tryInt("123a123")
}

运行结果为:

Value: 123123
Error: strconv.ParseInt: parsing "123a123": invalid syntax
  • 运行时异常
    • 当发生像数组下标越界或类型断言失败这样的运行错误时,会触发运行时panic,伴随着程序的崩溃抛出一个runtime.Error接口类型的值。
    • panic可以直接从代码初始化:当程序不能继续运行时,可以使用panic函数产生一个中止程序的运行时错误。
panic("A severe error occurred: stopping the program!")

运行结果为:

Starting the program
panic: A severe error occurred: stopping the program!
......
  • panic中恢复
    • 内建函数recover被用于从panic或错误场景中恢复。
    • recover只能在defer修饰的函数中使用:panic会导致栈被展开直到defer修饰的recover()被调用或者程序中止。
package main
import "fmt"
func main() {
   fmt.Println("Starting the program")
	defer func() {
		err := recover()
		if err != nil {
			fmt.Printf("捕捉异常: %T %v\n", err, err)
			fmt.Println("Recover the program")
		}
	}()
    panic("抛出异常")
    fmt.Println("Ending the program")
}

运行结果为:

Starting the program
捕捉异常: string 抛出异常
Recover the program
END .

相关系列文章

×