上一篇: [链接] ToC [链接] [链接] [链接] [链接] [链接] [链接] Go 边看边练 -《Go 学习笔记》系列(七)- 错误处理 Go 边看边练 -《Go 学习笔记》系列(八)- 数组、切片 Go 边看边练 -《Go 学习笔记》系列(九)- Map、结构体 Go 边看边练 -《Go 学习笔记》系列(十 ..

Go 边看边练 -《Go 学习笔记》系列(七)

本贴最后更新于 1360 天前,其中的信息可能已经时移世异

上一篇: Go 边看边练 -《Go 学习笔记》系列(六)


ToC


3.5 延迟调用

关键字 defer 用于注册延迟调用。这些调用直到 return 前才被执行,通常⽤用于释放资源或错误处理。

func test() error {
	f, err := os.Create("test.txt")
    if err != nil { return err }
    
    defer f.Close() // 注册调用,而不是注册函数。必须提供参数,哪怕为空。
    
    f.WriteString("Hello, World!")        
    return nil
}

多个 defer 注册,按 FILO 次序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。

延迟调用参数在注册时求值或复制,可用指针或闭包 "延迟" 读取。

滥用 defer 可能会导致性能问题,尤其是在一个 "大循环" 里。

var lock sync.Mutex
  	func test() {
  	lock.Lock()
  	lock.Unlock()
}

func testdefer() {
	lock.Lock()
	defer lock.Unlock()
}

func BenchmarkTest(b *testing.B) {
	for i := 0; i < b.N; i++ {
		test()
	}
}

func BenchmarkTestDefer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		testdefer()
	}
}

输出:

BenchmarkTest" 50000000 43 ns/op
BenchmarkTestDefer 20000000 128 ns/op

3.6 错误处理

没有结构化异常,使用 panic 抛出错误,recover 捕获错误。

func test() {
	defer func() {
		if err := recover(); err != nil {
			println(err.(string)) // 将 interface{} 转型为具体类型。
		}
	}()

	panic("panic error!")
}

由于 panicrecover 参数类型为 interface{},因此可抛出任何类型对象。

func panic(v interface{})
func recover() interface{}

延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获。

func test() {
	defer func() {
		fmt.Println(recover())
	}()

	defer func() {
		panic("defer panic")
	}()

	panic("test panic")
}

func main() {
	test()
}

输出:

defer panic

捕获函数 recover 只有在延迟调用内直接调用才会终止错误,否则总是返回 nil。任何未捕获的错误都会沿调用堆栈向外传递。

func test() {
	defer recover() // 无效!
	defer fmt.Println(recover()) // 无效!
	defer func() {
		func() {
			println("defer inner")
			recover() // 无效!
		}()
	}()
    
	panic("test panic")
}

func main() {
	test()
}

输出:

defer inner
<nil>
panic: test panic

使用延迟匿名函数或下面这样都是有效的。

func except() {
	recover()
}

func test() {
	defer except()
	panic("test panic")
}

如果需要保护代码片段,可将代码块重构成匿名函数,如此可确保后续代码被执行。

func test(x, y int) {
	var z int
	
    func() {
		defer func() {
			if recover() != nil { z = 0 }
		}()

		z = x / y
		return
	}()

	println("x / y =", z)
}

除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态。

type error interface {
	Error() string
}

标准库 errors.Newfmt.Errorf 函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。

如何区别使用 panicerror 两种方式?惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error

下一篇: Go 边看边练 -《Go 学习笔记》系列(八)



社区小贴士

  • 关注标签 [golang] 可以方便查看 Go 相关帖子
  • 关注作者后如有新帖将会收到通知

打赏 50 积分后可见  
50 积分
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    319 引用 • 1158 回帖 • 731 关注
  • 教程
    78 引用 • 344 回帖 • 3 关注
  • 雨痕
    14 引用 • 62 回帖 • 3 关注
3 回帖   
请输入回帖内容...